Forum: Mikrocontroller und Digitale Elektronik Atmega162 Timer


von BluBB (Gast)


Lesenswert?

Guten Abend zusammen,
ich habe da ein kleines Problem ;) Ich soll für einen Atmega162 (Takt 
16MHz) ein Programm in C schreiben das eine LED bei an PortB0 mit 10HZ 
blinken lässt.
Nachdem ich ewig gebraucht habe um den vorteiler auszurechnen (garnicht 
so einfach wenn man das noch nie gemacht ha) Bin ich zu dem Ergebnis 
gekommen dass ein Teiler von 6,25 benötigt wird. Da es diesen aber nicht 
gibt verwende ich einen Vorteiler von 8!
Jetzt gibt es ja die Möglichkeit den Timer im CTC modus laufen zu lassen 
und ihn nur zu einem bestimmten wert unter 255 zählen zu lassen, damit 
ich doch auch die gewünschte blinkfrequenz von 10HZ komme.
Leider weiss ich nicht genau wie ich das jetzt in den Code integrieren 
kann, sollte dieser überhaupt ansatzweise richtig sein.
Außerdem wäre ich für ein Beispiel oder so für den Zähleraufruf in der 
Main sehr dankbar.

Der Code:

#include <avr/io.h>
#include <avr/interrupt.h>

void init ()
{
//Led auf PORT B
DDRB |=0x01;
PORTB |=0x00;

//CTC von Timer1 aktivieren
TCCR0 &= ~(1<<WGM00);
TCCR0 |=  (1<<WGM01);

TCCR0 |=  (1<<CLKPS0);
TCCR0 |=  (1<<CLKPS1);
TCCR0 &= ~(1<<CLKPS2);
TCCR0 &= ~(1<<CLKPS3);

//Keine ahnung ob die nächsten 3 Zeilen stimmen...
OCR0=1
TIMSK |= (1<<OCIE0);
sei();
}

int main()
{
init();
while(1)
{
PORTB |= (1<<0x01); //LED AN
//Hier müsste ich dann irgendwie den Timer mit 10ms aufrufen
PORTB &= (1<<0x01); //LED AUS
//Hier wieder dem Timer aufrufen
}
}

von spess53 (Gast)


Lesenswert?

Hi

>Nachdem ich ewig gebraucht habe um den vorteiler auszurechnen (garnicht
>so einfach wenn man das noch nie gemacht ha) Bin ich zu dem Ergebnis
>gekommen dass ein Teiler von 6,25 benötigt wird. Da es diesen aber nicht
>gibt verwende ich einen Vorteiler von 8!

10Hz entspricht einer Periodendauer von 100ms (50ms an, 50ms aus). Der 
Timer0 (8 Bit) hat aber bei 16MHz und Vorteiler 8, eine Overflow-Zeit 
von 128µs. Selbst bei Vorteiler 1024 kommst du auf maximal 16,36ms. Die 
50ms kannst du also nicht so einfach erreichen.

Bist du an Timer0 gebunden? Mit dem Timer1 wäre es zumindest für dich 
leichter.

>//Keine ahnung ob die nächsten 3 Zeilen stimmen...
>OCR0=1
>TIMSK |= (1<<OCIE0);
>sei();

Ohne Interrupservicetroutine führt das unweigerlich zu Absturz.

MfG Spess

von BluBB (Gast)


Lesenswert?

Nein ich bin nicht an den Timer0 gebunden!
Also soll ich lieber den Timer1 mit Fast PWM benutzen?

Wenn nich und ich jetzt einen 16Bit Timer benutze, der bis 65536 Zählt 
komme ich auf einen Vorteiler von 3,125 (Wenn die Formel stimmt die ich 
habe, oder ich das richtige einsetze...)
Was die ISR angeht. Muss das ganze also so aussehen, oder hab ich das 
ganze jetzt nurnoch verschlimmert und ich sollte am besten wieder bei 0 
anfangen?
Das problem denau ich jetzt meinen Timer in der Main() aufrufe konnte 
ich bis jetzt auch noch nicht lösen. Hat jemand dazu noch nen Tipp oder 
nen Codebeispiel?


Neuer CODE:

#include <avr/io.h>
#include <avr/interrupt.h>

void init ()
{
//Led auf PORT B
DDRB |=0x01;
PORTB |=0x00;

//CTC von Timer1 aktivieren
TCCR1A &= ~(1<<WGM00);
TCCR1A |=  (1<<WGM01);

TCCR1A |=  (1<<CLKPS0);
TCCR1A |=  (1<<CLKPS1);
TCCR1A &= ~(1<<CLKPS2);
TCCR1A &= ~(1<<CLKPS3);

//Keine ahnung ob die nächsten 3 Zeilen stimmen...
OCR0=1
TIMSK |= (1<<OCIE0);
sei();
}
ISR(TIMER1_COMP_vect) {
  PORTD &= ~0x01;

int main()
{
init();
while(1)
{
PORTB |= (1<<0x01); //LED AN
//Hier müsste ich dann irgendwie den Timer mit 10ms aufrufen... habe 
aber bis jetzt keine Idee wie!
PORTB &= (1<<0x01); //LED AUS
//Hier wieder dem Timer aufrufen
}
}

von Michael U. (amiga)


Lesenswert?

Hallo,

Du hast 16000000Hz Takt und willst 10Hz.
Für den CTC mit Hardware-Toggle am Pin brauchst Du 20Hz, einmal von L 
auf H schalten und einmal zurück für Deine 10Hz.
16000000/20 sind 8000000 Teilerfaktor.
Das durch die möglichen Vorteiler teilen, bis das Ergebnis ganzzahlig 
ist.
Ich nehme gleich mal den 16Bit Timer.
800000/1024 = 781,25 geht nicht
800000/256 = 3125 ok.
Vorteiler also 256, Comaparewert 3125 - 1 = 3124
Macht 10Hz in Hardware.

Gruß aus Berlin
Michael

von spess53 (Gast)


Lesenswert?

Hi

>Wenn nich und ich jetzt einen 16Bit Timer benutze, der bis 65536 Zählt
>komme ich auf einen Vorteiler von 3,125 (Wenn die Formel stimmt die ich
>habe, oder ich das richtige einsetze...)

Dafür gibt es ja CTC. Damit lässt sich der Topwert des Timers 
einstellen.

mit

WGM-Bits auf Mode 4
OCR1A = 0x30D3

läuft der Timer alle 50ms über und fängt wieder bei Null an. Wenn der 
OC1A-Interrupt freigegeben ist wird dieser dann ausgelöst. In der ISR 
invertierst du dann das Portpin und fertig.

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

Fang erst mal damit an, deinen Code einzurücken!
Dazu musst du:
dich mit dir selbst auf ein Schema einigen, wie genau du die { } setzen 
willst.

Sagen wir mal: die { kommt immer in die nächste Zeile und steht alleine. 
Nach der { wird der Code der innerhalb des Blocks steht um 2 Leerzeichen 
eingerückt. Die Einrückung wird erst durch das } wieder aufgehoben, 
wobei die } in derselben Spalte steht, in der auch die { stand

Dann sieht dein Code so aus:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
void init ()
5
{
6
  //Led auf PORT B
7
  DDRB |=0x01;
8
  PORTB |=0x00;
9
10
  //CTC von Timer1 aktivieren
11
  TCCR1A &= ~(1<<WGM00);
12
  TCCR1A |=  (1<<WGM01);
13
14
  TCCR1A |=  (1<<CLKPS0);
15
  TCCR1A |=  (1<<CLKPS1);
16
  TCCR1A &= ~(1<<CLKPS2);
17
  TCCR1A &= ~(1<<CLKPS3);
18
19
  //Keine ahnung ob die nächsten 3 Zeilen stimmen...
20
  OCR0=1
21
  TIMSK |= (1<<OCIE0);
22
  sei();
23
}
24
25
ISR(TIMER1_COMP_vect)
26
{
27
  PORTD &= ~0x01;
28
29
  int main()
30
  {
31
    init();
32
    while(1)
33
    {
34
      PORTB |= (1<<0x01); //LED AN
35
      //Hier müsste ich dann irgendwie den Timer mit 10ms aufrufen... habe aber bis jetzt keine Idee wie!
36
      PORTB &= (1<<0x01); //LED AUS
37
      //Hier wieder dem Timer aufrufen
38
    }
39
  }
40
}

Durch diese Struktur sieht man sofort, dass deine Funktion main() 
innerhalb der ISR geschachtelt wurde. Zumindest würde es das, wenn es in 
C gehen würde, denn in C kann man keine Funtionen in andere Funktionen 
schachteln (auch wenn der gcc das kann). Auf jeden Fall ist das nicht 
das, was du wolltest. Wo liegt der Fehler? Nun, du hast die } von der 
ISR Funktion vergessen.
Durch die Einrückung sieht man solche Dinge! Daher ist eine Einrückung 
nicht einfach nur etwas, damit der Code schöner aussieht, sondern ist 
ein wichtiges Hilfsmittel um nicht den Überblick zu verlieren.


Zum Code.
Du rufst keinen Timer auf! Sondern: Du stellst den Timer in eine 
bestimmte Betriebsart ein und wenn bestimmte Bedingungen vorliegen (zb 
der Timer hat seinen Endwert erreicht), dann ruft der Timer die 
ISR-Funktion auf!
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
ISR(TIMER1_COMPA_vect)
5
{
6
  PORTB ^= 0x01;
7
}
8
9
int main()
10
{
11
  //Led auf PORT B
12
  DDRB |= 0x01;
13
14
  // CTC Modus
15
  // Vorteiler 1024
16
  TCCR1B = (1<<WGM12) | (1<<CS12) | (1<<CS10);
17
  OCR1A = 780;
18
  TIMSK |= (1<<OCIE1A);
19
  sei();
20
21
  while(1)
22
  {
23
  }
24
}

Soweit blinkt die LED.

Dein Zähler zählt mit
16000000 / 1024 = 15625

Das heist, wenn du nichts weiter tust, dann würde der Timer in 1 Sekunde 
bis 15625 zählen.
Du willst 10 Hz haben, also brauchst du 20 ISR Aufrufe in der Sekunde 
(10 fürs einschalten, 10 fürs ausschalten). D.h. von einem ISR AUfruf 
zum nächsten dürfen 1 / 20 = 0.05 Sekunden vergehen.

Wenn dein Timer in 1 Sekunde bis 15625 zählen würde, wie weit kommt er 
dann in 0.05 Sekunden?
Na genau bis 15625 * 0.05 = 781.25

Und daher setzt ich diesen Wert als Vergleichswert ins 
Vergleichsregister.

Jetzt zählt der Zähler mit einer bestimmten Taktrate (vorgegeben durch 
Taktfrequenz und Vorteiler) bis 780. Danach löst er einen Compare Match 
aus welcher wiederrum die ISR Funktion aufruft. Wegen dem CTC Modus 
zählt der Timer aber nicht bei 781, 782, ... weiter, sondern fängt 
wieder bei 0 an.
Der Timer zählt also laufend
0, 1, 2, 3, ..., 777, 778, 779, 780, 0, 1, 2, 3, .... , 777, 778, 779, 
780
und die 780 sind so gewählt, dass der Timer für einmal von 0 bis 780 
zählen 0.05 Sekunden braucht.

Bei einem andern Vorteiler ergibt sich ein anderer Vergleichswert. Aber 
da es nicht allzuviele Vorteiler gibt, kannst du ja einfach mal alle 
durchprobieren und nachsehen, ob es einen gibt, bei dem sich die 
Berechnung des Vergleichswertes auf eine glatte 0 in den 
Nachkommastellen ausgeht.

Nichts gegen die Formeln im Datenblatt. Aber IMHO ist es besser, du 
machst dir klar, was der Timer eigentlich macht und was das für deine 
Zahlen bedeutet. Dann brauchst du auch keine Formeln mehr lernen oder 
nachschlagen.

von BluBB (Gast)


Lesenswert?

Hallo,
VIELEN VIELEN Dank für die super Erklärung.
Habe eben nochmal nachgerechnet und bei einem Vorteiler von 256 bekomme 
ich eine glatten vergleichswert von 3125 raus.
Werde den Code morgen mal auf seine Funktionsfähigkeit testen (Wirklich 
erschreckend, dass der Code von Karl Heinz so viel einfacher aussieht 
als mein erster versuch...;) )

von Guru (Gast)


Lesenswert?

>denn in C kann man keine Funtionen in andere Funktionen
>schachteln (auch wenn der gcc das kann)

Wurks! Manchmal kann der gcc ein bischen viel.
Wenn das Kernighan und Ritchie wüssten... :-)
Naja. Wollen wir mal nicht so undankbar sein.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.