PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Problem mit dem Timer2 eines ATMega8



sloti
01.07.2008, 17:11
Hi,

ich habe wie gesagt ein Problem mit dem 8 bit Timer meines ATMega8. Ich betreibe ihn mit einem externen Quarz bei 3,6864 MHz. Der Timer soll einen Interupt bei einem Überlauf auslösen und das möglichst 1000 mal die Sekunde. Dafür habe ich mit dem RN-Tool rnAVR einen prescaler von 64 und einen preloader von 198 ausgerechnet (ist das richtig?). Nun hab ich allerdings das Problem, dass wenn ich eine Lampe immer bei der gleichen
Anzahl von Interrupts an bzw. ausschalte, immer ein Abstand von ca. drei Sekunden habe auch wenn ich den Wert von 255 auf z.B. 85 runtersetze. Zudem hab ich noch eine Frage die sich auf die Variable "countTimer2" bezieht: Die Variable müsste doch eigentlich, wenn die Berechnungen von rnAVR stimmen, nach 1 sek. den Wert 1000 haben, doch bei mir erreicht sie lediglich 255, also einen 8 bit Wert, aber die 8 bit beziehen sich doch nicht auf die Variable aus der ISR oder? Irgendwie läuft das ganze nicht so wie ich mir das Vorstelle. Hier ist noch mein Code. Nicht erschrecken das meiste ist für die Ansteuerung einses LCD nur die ersten drei Funktionen beziehen sich auf mein Timerproblem.




#define F_CPU 3686400 // Taktfrequenz des myAVR-Boards
#define CLOCK 3686400
#include <avr\io.h> // AVR Register und Konstantendefinitionen
#include <avr\interrupt.h>
#include <avr/delay.h>
#include <stdlib.h>

//----------------------------------------------------------------------


volatile int countTimer2; // Speichert den aktuellen Zählerwert
SIGNAL (SIG_OVERFLOW2) // ISR
{

countTimer2++;

}

void init (void)
{
DDRB= 0x00; // PortB Eingang
DDRC= 0xFF; // PortC Ausgang
PORTB |= (1<<PB0); // PortB.0 Pull up
TCCR2 = 0x04; // Prescaler von 64
TCNT2 = 198; // Vorladen mit 198
TIMSK |= (1<<TOIE2); // Interrupts aktivieren und damit Timer starten
sei(); // interupts erlauben
}

int get_time(void) // gibt die Anzahl der Überläufe wieder
{
return countTimer2;

}

void reset_timer(void) // stellt den Timer wieder auf Null
{
countTimer2=0;
TIMSK |= (1<<TOIE2);

}

void wait_ms(int miliSec)
{
_delay_loop_2( 1*(CLOCK/(1000/4)) * miliSec); // 4 Zyklen warteschleife
}

void lcd_send(char data)
{
// aktuelles RS ermitteln
char rs=PORTD;
rs&=4;
// High-Teil senden
char tmp=data;
tmp&=0xf0;
tmp|=rs;
PORTD=tmp;
// Schreibsignal
sbi(PORTD,3);
cbi(PORTD,3);
// Low-Teil senden
tmp=data;
tmp&=0x0f;
tmp*=16;
tmp|=rs;
PORTD=tmp;
// Schreibsignal
sbi(PORTD,3);
cbi(PORTD,3);
// verarbeiten lassen
wait_ms(1);
}
//---------------------------------------------------------------------------
// lcd_cmd(..) - sendet ein Kommando an LCD
// PE: cmd=Kommando-Byte
//---------------------------------------------------------------------------
void lcd_cmd(char cmd)
{
cbi(PORTD,2); // RS löschen = Kommando
lcd_send(cmd); // senden
}
//---------------------------------------------------------------------------
// lcd_write(..) - sendet ein Zeichen (Daten) an LCD
// PE: text=Zeichen
//---------------------------------------------------------------------------
void lcd_write(char text)
{
sbi(PORTD,2); // RS setzen = Daten
lcd_send(text); // senden
}
//---------------------------------------------------------------------------
// lcd_write(..) - sendet eine Zeichenkette an LCD
// Die Zeichenkette muss mit 0x00 abgeschlossen sein
// PE: pText=Zeiger auf Zeichenkette
//---------------------------------------------------------------------------
void lcd_write(char* pText)
{
while(pText[0]!=0)
{
lcd_write(pText[0]);
pText++;
}
}
//---------------------------------------------------------------------------
// lcd_write(..) - sendet eine Zeichenkette an LCD
// PE: pText=Zeiger auf Zeichenkette
// count=Anzahl der zu sendenden Zeichen
//---------------------------------------------------------------------------
void lcd_write(char* pText, int count)
{
while(count!=0)
{
lcd_write(pText[0]);
pText++;
count--;
}
}
//---------------------------------------------------------------------------
// lcd_home(..) - Cursor auf Position 1,1
//---------------------------------------------------------------------------
void lcd_home()
{
lcd_cmd(0x02);
wait_ms(2); // warten
}
//---------------------------------------------------------------------------
// lcd_clear(..) - löscht die Anzeige im LCD
//---------------------------------------------------------------------------
void lcd_clear()
{
lcd_cmd(0x01);
wait_ms(2); // warten
}
//---------------------------------------------------------------------------
// lcd_on(..) - schaltet das LCD an
//---------------------------------------------------------------------------
void lcd_on()
{
lcd_cmd(0x0E);
}
//---------------------------------------------------------------------------
// lcd_off(..) - schaltet das LCD aus
//---------------------------------------------------------------------------
void lcd_off()
{
lcd_cmd(0x08);
}
//---------------------------------------------------------------------------
// lcd_goto(..) - setzt die Cursorposition
// PE: row = Zeile 1..2
// col = Spalte 1..16
//---------------------------------------------------------------------------
void lcd_goto(int row, int col)
{
row--; // Null-basierend
row&=0x01; // sicherheitshalber
row*=0x40; // Zeile nach Bit 6 bringen
col--; // Null-basierend
col&=0x0f; // sicherheitshalber
char tmp=row|col;
tmp|=0x80; // Cursor setzen
lcd_cmd(tmp); // senden
}
//---------------------------------------------------------------------------
// lcd_init(..) - Schaltet die Ports und Initialisiert das LCD
//---------------------------------------------------------------------------
void lcd_init()
{

ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1);
TCCR1A=0xA1;
TCCR1B =0x02;
// Port D = Ausgang
DDRD=0xff;
PORTD=0;


// warten bist LCD-Controller gebootet
wait_ms(200);
// 4-BitModus einschalten
PORTD=0x20;
// Schreibsignal
sbi(PORTD,3);
cbi(PORTD,3);
wait_ms(5); // Zeit zum Umschalten lassen
// ab hier im 4-Bit-Modus
lcd_cmd(0x28); // Funktions-Set: 2 Zeilen, 5x7 Matrix, 4 Bit
//lcd_off();
lcd_cmd(0x06); // Entry Mode
lcd_on();
lcd_clear();
}


main ()
{
char string[3];
int time;


init();
lcd_init();

while(1)
{
if(get_time==255)
{
PORTC |=(1<<PC5);
}

else
{
PORTC &= ~(1<<PC5);
}







}

}

schon mal Danke im Vorraus ;)

mfg
Erik

SprinterSB
01.07.2008, 18:38
Das Vorladen wird nur 1x in init() gemacht, danach nicht mehr.

Das korrekte Register heisst OCR2 oder so.



void timer2_init (uint8_t ocr2)
{
// Mode #2 für Timer2 (Manual S. 115)
// und PRESCALE=8
TCCR2 = (1 << WGM21) | (1 << CS21);

// PoutputCompare für gewünschte Timer2 Frequenz
OCR2 = ocr2;

// OutputCompare-Interrupt A für Timer 2
TIMSK |= (1 << OCIE2);
}

sloti
01.07.2008, 18:46
Braucht man das Register OCR2 nicht nur wenn man den Compare Match modus benutzt?

linux_80
01.07.2008, 18:47
Hallo,

die 198 müssen bei jedem IRQ neu gesetzt werden, sonst zählt der immer von 0 bis 255 !
Deshalb die Zeile bei init() auch oben bei SIGNAL am Anfang einbauen.
Die Variable countTimer2 ist schon global, deshalb brauchts keine extra function (get_time) die den Wert zurückgibt.

SprinterSB
01.07.2008, 20:08
Braucht man das Register OCR2 nicht nur wenn man den Compare Match modus benutzt?

Es ermöglicht genauere Zeiten, weil man das TCNT2 nicht bei jedem Interrupt von Hand setzen muss, sondern die Hardware setzt TCNT2 beim Erreichen von OCR2 zurück. Voraussetzung ist natürlich der richtige Timer-Modus (Clear Timer on Compare Match, CTC).

Ceos
01.07.2008, 22:48
also prinzipiell würde ich sogar voschlagen statt mit 198 vorzuladen gleich CTC zu benutzen und das OCR2 register mit 57 einmal initialisieren und statt der SIG_OVERFLOW2 das SIG_COMPAREMATCH2 (oder wie hiess das) zu verwenden, was SprinterSB schon angedeutet hat