Timer 1 auch als Zeitbasis nutzen ohne Interrupts
(EN google-translate)
(PL google-translate)
Eine einfache Möglichkeit, eine Zeitsteuerung für die einzelnen Prozesse des Programms zu erhalten besteht in der Nutzung eines Timer als Zähler und das Abwarten dessen Überlaufs am Ende der Main-Loop.
Im Fall hier wird der bereits für den Antrieb (PWM-Geber) verwendete Timer 1 benutzt. Dieser Timer war hier bisher so konfiguriert, dass ein 10-Bit Phasen-korrektes PWM-Signal generiert wird (Modus3, Datenblatt Seite 107). Nun hat dieses den Nachteil, dass hier der Zähler hoch- und wieder herunter zählt und somit bei einem bestimmten Zählstand nicht erkennbar ist, ob er beim Herauf- oder Herabzählen erreicht wurde.
Darum wird nun das nicht Phasen-korrekte Fast-PWM-Signal auch mit 10 Bit verwendet (Modus 7). #pdf atmega32_doc2503.pdf atmega32_doc2503.pdf - Datenblatt zum ATmega32.
Bei dem nachfolgenden Programm wird ein Blinken mit 0,5Hertz auf einer LED des Fahrzeugs erzeugt, indem für 122 Main-Loop-Durchgänge diese an und für die darauffolgenden 122 ausgeschaltet wird. Ein Main-Loop-Durchgang erfolgt dabei mit...
Zählfrequenz = 8.000.000 / (64*1024) == 122,0703125Hz Da sich der PWM-Zyklus laut Datenblatt für Fast-PWM wie folgt berechnet und das Fahrzeut mit 8MHz getaktet ist: Zählfrequenz = fclock / (N*(TOP+1))
Code 0-1: Berechnung der Frequenz, mit der ein Main-Durchlauf abgearbeitet wird (siehe auch nachfolgenden Quelltext).
Man beachte die Warteschleife while(TCNT1<1023) PORTB|=0; am Ende des nachfolgenden Quelltextes in der Main-Loop:
// avr-gcc -O2 -mmcu=atmega32 syncprog.c -o syncprog.elf // avr-objcopy -O ihex -j .text -j .data syncprog.elf syncprog.hex // Upload über RobotLoader_20100712 #include<avr/io.h> // Zählfrequenz = fclock / (N*(TOP+1)) // Zählfrequenz = 8.000.000 / (64*1024) == 122,0703125Hz (einmal hochzählen) ungefähr 8ms pro Schleifendurchlauf void initialisiereMotore() { DDRC |= 0b00001100; //Richtung //10-Bit Phasen-korrektes PWM-Signal. TCCR1A = (1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0) | (0<<FOC1A) | (0<<FOC1B) | (1<<WGM11) | (1<<WGM10); TCCR1B = (0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (1<<WGM12) | (0<<CS12) | (1<<CS11) | (1<<CS10); DDRD |= (1<<PD5) | (1<<PD4); //auf Ausgang setzen. OCR1A = 0; //PWM-Breite setzen (entspricht für Servo auf 0 Grad). OCR1B = 0; //PWM-Breite setzen (entspricht für Servo auf 0 Grad). } void fahrt(int links, int rechts) //Wertebereich: +-1023 { if(links==0) { PORTC &= ~(1<<PC2); OCR1B = 0; } else if(links<0) { PORTC |= (1<<PC2); OCR1B = -links; } else { PORTC &= ~(1<<PC2); OCR1B = links; } if(rechts==0) { PORTC &= ~(1<<PC3); OCR1A = 0; } else if(rechts<0) { PORTC |= (1<<PC3); OCR1A = -rechts; } else { PORTC &= ~(1<<PC3); OCR1A = rechts; } } int main() { unsigned long pause=0; initialisiereMotore(); int ZAEHLER = 0; DDRC |= 0b01110000; while(1) //Durchlauf === 122,0703125Hz { /* for(pause=0;pause<300000;pause++) DDRB|=0; fahrt(1023,1023); //Geradeaus for(pause=0;pause<300000;pause++) DDRB|=0; fahrt(0,0); //stop for(pause=0;pause<300000;pause++) DDRB|=0; fahrt(200,200); //langsame Geradeaus for(pause=0;pause<300000;pause++) DDRB|=0; fahrt(0,0); //stop for(pause=0;pause<300000;pause++) DDRB|=0; fahrt(-1023,1023); //Links-Drehung for(pause=0;pause<300000;pause++) DDRB|=0; fahrt(0,0); //stop */ ZAEHLER++; ZAEHLER%=244; //Blinken mit 0,5Hz: if(ZAEHLER<122) // 1 Sekunde an { PORTC |= 0b01110000; } else //dann 1 Sekunde aus. { PORTC &= ~0b01110000; } while(TCNT1<1023) PORTB|=0; //Immer warten, bis der Zähler den oberen Zählstand erreicht hat. //Der Zählvorgang ist langsam gegenüber dieser while-Schleife, so wird 1023 in jedem Fall "erwischt". } }
Code 0-2: Beispielprogramm - Nur Blinken (Fahren auskommentiert) mit 0,5Hz, siehe while-Schleife in Main-Loop.