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.
SUMOBOT3.zip - Obiges Programm als Quelltext und .hex-Datei.