Forum: Mikrocontroller und Digitale Elektronik Atmega8 Timer Prescale LED?


von Digit-22 N. (digit-22)


Lesenswert?

Hej Leute



nach einer langen Pause (wegen Abschlussprüfungen und Nachwuchs) möchte 
ich mich wieder mit meinem Atmega8 beschäftigen. Ich habe schon mit 
einfachen ein und ausschalten von Ausgängen (LED Blink Blink) im 
Verbindung mit Taster und und gearbeitet. Nun möchte ich mich langsam an 
Timer und später über eine externe PWM z.B. LED Blinkfrequenz Regelungen 
machen.
Das größte Problem, was ich habe ist, ich lese und lese, Google und 
google schon ewig doch blicke da einfach nicht durch. Es gab noch kein 
AHAAA Effekt. Erst dann hat man so etwas verstanden und kann es 
einsätzen. So geht es zu mindest mir so.

Bevor mich irgendjemand lyncht da das Thema schon 1000000 mal 
angesprochen wurde, hab Verständnis bin Anfänger im Mµ Bereich.

Ich wäre wirklich wirklich sehr sehr dankbar wenn mir irgendwer an Hand 
eines C Programms ein ganz einfaches Beispiel z.B. verschiedene LEDs mit 
verschiedenen Frequenzen blinken lassen, schritt für Schritt erklären 
würde. Nur das ist eine Vorstellung habe, worum es da genau geht.

Womit fange ich an? Was muss ich festlegen und / Oder Aktivieren?
Wonach muss ich im Datenblatt suchen...?

Meine Hardware:

Atmega8 mit externen Quarz 4MHz.
Alles aufm Steckbrett.
LEDs, Widerstände, Taster... alles vorhanden.
Oszi, Multimeter... auch alles da.


Ich bedanke mich schonmal im voraus.


LG
Shabi

von Al3ko -. (al3ko)


Lesenswert?

Shabi N. schrieb:
> Ich wäre wirklich wirklich sehr sehr dankbar wenn mir irgendwer an Hand
> eines C Programms ein ganz einfaches Beispiel z.B. verschiedene LEDs mit
> verschiedenen Frequenzen blinken lassen, schritt für Schritt erklären
> würde. Nur das ist eine Vorstellung habe, worum es da genau geht.
Arbeite diese Seite durch.

https://sites.google.com/site/qeewiki/books/avr-guide

Fang bei den Timern an und arbeite dich zur PWM vor. Dann kannst du 
deine LED blinken lassen in verschiedenen Frequenzen.

Cheers,
al3ko

von Karl H. (kbuchegg)


Lesenswert?

Für eine kleine Einführung in Timer, die die wichtigsten Dinge aus der 
praktischen Sicht heraus beleuchtet:
FAQ: Timer

von Peter D. (peda)


Lesenswert?

Shabi N. schrieb:
> wenn mir irgendwer an Hand
> eines C Programms ein ganz einfaches Beispiel z.B. verschiedene LEDs mit
> verschiedenen Frequenzen blinken lassen

Das wird Dir nichts nützen.
Der Trick beim Programmieren besteht nicht darin, ein paar Zeilen C 
hinzuschreiben, sondern sich erstmal ein Konzept zu überlegen, d.h. in 
Worten zu beschreiben, wie der Programmablauf erfolgen soll.

Wenn man das vollständig und eindeutig hinkriegt, ist der eigentliche 
Code nur noch etwa 10..20% der Arbeit.

Auch muß man erstmal einige Grundkonzepte lernen, wie Mainloop, 
Bitmanipulation, Entprellung, Zählvariablen, Timer, Statemaschine usw.

Das Falschestes ist, sofort drauflos zu programmieren, das muß unbedingt 
schiefgehen.


Peter

von Digit-22 N. (digit-22)


Lesenswert?

Hej Leute

Danke für die Links die sind echt gut. So lngsam klappt es mit ´dem 
Timer.
Hab es geschafft eine LED zum Blinken zu bringen...
Der folgende Code schaltet meine LED ein und aus und da hätte ich noch 
ein paar generelle FRagen:


#define F_CPU 4000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <inttypes.h>
#include <avr/interrupt.h>

int main(void)
{
  DDRB = 0xff;
  PORTB =0xff;

  TIMSK = (1 << TOIE0);
  sei ();
  TCCR0  = (1 << CS02) | (1 << CS00);

  while(1)
  {
        }
}

ISR (TIMER0_OVF_vect)
{
  PORTB |= 1<<PB5;
  //_delay_ms(200);
  PORTB &= ~(1<<PB5);
  //_delay_ms(200);
}


Ich benutze den Atmega8 mit 4MHz und einen Prescale von 1024.
Das ergibt bei 4 MHz:
4000000/1024= 3906,25Hz
3906,25/256=15,25 mal/s
1/15,25=0,065s pro Durchlauf.

Meine LED Blinkt natürlich viel zu schnell. Hab noch nen kleinen Buzzer 
angeschlossen so das ich das ticken hören kann.

Meine 1. Frage:
Geht denn nicht mehr als 1024 Vorteiler?

2. wie mache ich es denn wenn ich genau auf 1 ms pro Durchlauf haben 
will??
Bei 4MHz passt keiner von den Prescales.

2.Wenn ich die delay_ms mit einbaue, blinkt die LED schön im Takt so das 
man das auch sehen kann. Was passiert denn jetzt genau? Wenn mein Timer 
einen Overflow erreicht wird mein Interrupt gestartet. Wartet jetzt mein 
Timer bis 200ms bzw 400ms vorbei sind und zählt dann wieder weiter??

Danke schon mal im voraus.


LG
Shabi

von spess53 (Gast)


Lesenswert?

Hi

>Geht denn nicht mehr als 1024 Vorteiler?

Nicht beim ATMega8.

>2. wie mache ich es denn wenn ich genau auf 1 ms pro Durchlauf haben
>will??
>Bei 4MHz passt keiner von den Prescales.

CTC-Mode des Timers.

>2.Wenn ich die delay_ms mit einbaue, blinkt die LED schön im Takt so das
>man das auch sehen kann. Was passiert denn jetzt genau? Wenn mein Timer
>einen Overflow erreicht wird mein Interrupt gestartet. Wartet jetzt mein
>Timer bis 200ms bzw 400ms vorbei sind und zählt dann wieder weiter??

Er zählt weiter. Delays in Interrupt-Routinen sind Unsinn.

MfG Spess

von Peter D. (peda)


Lesenswert?

Shabi N. schrieb:
> Wartet jetzt mein
> Timer bis 200ms bzw 400ms vorbei sind und zählt dann wieder weiter??

Dann könnte man die Timer in die Tonne treten, sie wären nutzlos.

Der Witz an den Timern ist ja gerade, daß sie unabhängig von der 
Programmausführung laufen. Nur so kann man genaue Uhren bauen.

Wenn Dir der Zählbereich des Timers nicht reicht, dann zähl einfach noch 
ne Variable im Interrupthandler.
1
ISR (TIMER0_OVF_vect)
2
{
3
  static uint32_t sw_timer = F_CPU * 60UUL * 60 * 24 * 365 / 1024 / 256;
4
  if( --sw_timer )
5
    return;
6
  sw_timer = F_CPU * 60UUL * 60 * 24 * 365 / 1024 / 256;
7
  // mache was nach einem Jahr
8
}

Peter

von Digit-22 N. (digit-22)


Lesenswert?

hhhhmm  Ok

ich hab jetzt mak etwas mit CTC rumgespielt doch es klappt noch nicht so 
wie ich es gern hätte.

hier mal ein Beispiel:

#define F_CPU 4000000UL

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

int main()
{
  DDRB = 0xff;
  PORTB =0xff;

  TIMSK = (1 << OCIE2);
  OCR2 = 241 - 1;
  TCCR2  = (1 << WGM21) | (1 << CS22) | (1 << CS21) | (1 << CS20);
  sei ();
  while(1)
  {
  }
}
ISR (TIMER2_COMP_vect)
{
  PORTB |= 1<<PB5;
  PORTB &= ~(1<<PB5);
}

Nehmen wir an ich will das meine LED 1 mal in einer Sekunde Blinkt.
Dann mache ich doch erstmal folgendes:
4000000/1024= 3906,25 Hz.mit dieser Frequenz zählt mein Timer.
Dann mach ich ja 3906,25/256= 15,25 So oft zäht der Timer von 0 - 255/s.

Wenn ich jetzt 1Hz haben möchte, wie muss ich denn jetzt weiter machen??
Ist es denn überhaubt möglich mit den reinen Timer Funktionen sowas zu 
realisieren? Theoretisch müsste ich doch 3906,25/3906,25 ´machen damit 1 
HZ raus kommt.

Bin etwas verwirrt. Die Zahl bei (OCR2 = 241 - 1) ist nur ein Beispiel 
und funktioniert auch. Wenn ich z.B. 21 - 1 eingebe, geht die Frequenz 
wieder hoch.

HHHmm  Grübel grübel.


LG
Shabi

von John (Gast)


Lesenswert?

Shabi N. schrieb:
> Wenn ich jetzt 1Hz haben möchte, wie muss ich denn jetzt weiter machen??
> Ist es denn überhaubt möglich mit den reinen Timer Funktionen sowas zu
> realisieren? Theoretisch müsste ich doch 3906,25/3906,25 ´machen damit 1
> HZ raus kommt.

Um mit einem 8-bit Timer auf 1Hz zu kommen ist das Einfachste wenn Du 
einen Quarz nimmst, dessen Frequenz sich durch Binärteilung (:2) auf 1Hz 
teilen lässt.
http://www.reichelt.de/Quarze/2-097152-HC18/3//index.html?ACTION=3&GROUPID=3173&ARTICLE=1850&SHOW=1&START=0&OFFSET=500&;

Oder 15Hz:
http://www.reichelt.de/Quarze/3-9321-HC18/3//index.html?ACTION=3&GROUPID=3173&ARTICLE=2382&SHOW=1&START=0&OFFSET=500&;

von Peter D. (peda)


Lesenswert?

Shabi N. schrieb:
> Wenn ich jetzt 1Hz haben möchte

Dann erinnere Dich an die Grundschule, Faktorenzerlegung:

4000000
= 2^8 * 5^6
= 256 * 125 * 125
  Prescaler, Timer, Variable


Peter

von Fabian O. (xfr)


Lesenswert?

Shabi N. schrieb:
> Wenn ich jetzt 1Hz haben möchte, wie muss ich denn jetzt weiter machen??
> Ist es denn überhaubt möglich mit den reinen Timer Funktionen sowas zu
> realisieren? Theoretisch müsste ich doch 3906,25/3906,25 ´machen damit 1
> HZ raus kommt.

Du darfst den Timer halt nicht stur von 0 bis 255 zählen lassen, sondern 
bis zu einem anderen Maximalwert. Wenn er beispielsweise von 0 bis 194 
zählst, dann macht er er das 3906,25 / 195 = 20 Mal pro Sekunde. Die ISR 
wird also alle 50 ms aufgerufen. Darin erhöhst (oder verringerst) Du 
jedes Mal eine Variable. Wenn die ISR 20 Mal aufgerufen wurde, ist eine 
Sekunde vergangen und Du schaltest die LED um.

von Digit-22 N. (digit-22)


Lesenswert?

hhhm das hört sich gut an. Ich müsste dann eine Variable bei jeden 
durchlauf um einen Wert hochzählen lassen z.B. i++. Und wenn i==20 dann 
Led an und aus und i wieder auf 0.
Nur stell ich mir die Frage, i muss ja vorher einen wert haben wie z.B. 
0.
Dann würde ja aber bei jedem durchgang i erst 0 und dann wieder 1 sein.

Ich muss doch irgendwie einmalig i den wert 0 zuweisen so das i nur beim 
Programm start 0 ist und ab dann immer hochgezählt wird.

HHHm wie könnte man das lösen?


shabi

von Fabian O. (xfr)


Lesenswert?

Das geht mit einer Modulvariable:
1
static uint8_t counter = 0;
2
3
ISR()
4
{
5
  counter++;
6
  if (counter == 20) {
7
    counter = 0;
8
    led_toggle();
9
  }
10
}

oder einer statischen Funktionsvariable:
1
ISR()
2
{
3
  static uint8_t counter = 0;
4
  counter++;
5
  if (counter == 20) {
6
    counter = 0;
7
    led_toggle();
8
  }
9
}

Der Unterschied ist, dass man im ersten Fall auch von anderen Funktionen 
(innerhalb der gleichen C-Datei) auf counter zugreifen kann, im zweiten 
Fall counter dagegen nur in der ISR sichtbar ist.

von Digit-22 N. (digit-22)


Lesenswert?

Ich hab es mal ausprobiert aber irgendwas stimmt da noch nicht.
sobal ich spannung am µC anlege dauert es 1 Sek und die LED geht an und 
das wars. es passiert nichts mehr. Als ob mein Timer nur einen Overflow 
macht. Die Zeit scheint zu stimmen.




#define F_CPU 4000000UL

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

int main()
{
  DDRB = 0xff;
  PORTB =0xff;

  TIMSK = (1 << OCIE2);
  OCR2 = 195 - 1;
  TCCR2  = (1 << WGM21) | (1 << CS22) | (1 << CS21) | (1 << CS20);

        sei ();
  while(1)
  {
  }
}
ISR (TIMER2_COMP_vect)
{
  static uint8_t counter = 0;
  counter++;

  if (counter == 20)
  {
  counter = 0;
  PORTB |= 1<<PB5;
  PORTB &= ~(1<<PB5);
  }
}


Wo könnte denn der Fehler sein? Vom Compiler bekomme ich keine 
Fehlermeldung oder so.


LG Shabi

von Fabian O. (xfr)


Lesenswert?

Du schaltest die LED in der ISR aus und danach sofort wieder ein. Sie 
ist also für ca. einen Takt aus und 3999999 Takte lang an ...

von Digit-22 N. (digit-22)


Lesenswert?

So hat geklapt hier meine Lösung.

1
#define F_CPU 4000000UL
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
5
int main()
6
{
7
  
8
  DDRB = 0xff;
9
  PORTB =0xff;
10
11
  TIMSK = (1 << OCIE2);
12
  OCR2 = 195 - 1;
13
  TCCR2  = (1 << WGM21) | (1 << CS22) | (1 << CS21) | (1 << CS20);
14
  sei ();
15
  while(1)
16
  {
17
  }
18
}
19
20
ISR (TIMER2_COMP_vect)
21
{
22
  static uint8_t counter = 0;
23
  counter++;
24
  
25
  if (counter == 20)
26
  {
27
    PORTB |= 1<<PB5;
28
    counter++;
29
  }
30
  if (counter == 40)  
31
  {
32
    PORTB &= ~(1<<PB5);
33
    counter = 0;
34
  }
35
36
}
LG
Shabi

von Karl H. (kbuchegg)


Lesenswert?

Shabi N. schrieb:
> So hat geklapt hier meine Lösung.
>

Wenn man es ganz genau nimmt, dann ist deine Led-Auszeit, wegen der 
zusätzlichen Counter Erhöhung hier

>   if (counter == 20)
>   {
>     PORTB |= 1<<PB5;
>     counter++;
>   }

um eine Winzigkeit kürzer als die An-zeit
(Annahme led leuchtet bei einer 0 am Port)

Wenn du auf explizites ein-/ausschalten verzichtest und statt dessen den 
Pin toggelst
1
ISR (TIMER2_COMP_vect)
2
{
3
  static uint8_t counter = 0;
4
  counter++;
5
  
6
  if (counter == 20)
7
  {
8
    PORTB ^= 1<<PB5;
9
    counter = 0;
10
  }
11
12
}

dann erledigt sich dieses "Problem" ganz von alleine. Die An-Auszeiten 
sind dann trivialerweise EXAKT gleich lang. (Im Rahmen der Interrupt 
Latenz)

von Digit-22 N. (digit-22)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wenn du auf explizites ein-/ausschalten verzichtest und statt dessen den
> Pin toggelst
> ISR (TIMER2_COMP_vect)
> {
>   static uint8_t counter = 0;
>   counter++;
>
>   if (counter == 20)
>   {
>     PORTB ^= 1<<PB5;
>     counter = 0;
>   }
>
> }

Hallo Karl Heinz

Das funktioniert. Aber was passiert da genau? Und was ist Toggeln??
1. Counter ist 0
2. Counter wird um 1 erhöht
3. Wenn Counter == 20
4. Dann diese Zeile   PORTB ^= 1<<PB5;  (Was passiert hier genau?
   Diese Schreibweise kenne ich noch nicht)
5. Counter wieder 0


Danke im voraus

LG
Shabi

von Karl H. (kbuchegg)


Lesenswert?

Shabi N. schrieb:
> Karl Heinz Buchegger schrieb:
>> Wenn du auf explizites ein-/ausschalten verzichtest und statt dessen den
>> Pin toggelst
>> ISR (TIMER2_COMP_vect)
>> {
>>   static uint8_t counter = 0;
>>   counter++;
>>
>>   if (counter == 20)
>>   {
>>     PORTB ^= 1<<PB5;
>>     counter = 0;
>>   }
>>
>> }
>
> Hallo Karl Heinz
>
> Das funktioniert. Aber was passiert da genau? Und was ist Toggeln??

Toggeln ist einfach nur der technische Ausdruck für 'Umschalten'.



> 1. Counter ist 0
> 2. Counter wird um 1 erhöht
> 3. Wenn Counter == 20
> 4. Dann diese Zeile   PORTB ^= 1<<PB5;  (Was passiert hier genau?
>    Diese Schreibweise kenne ich noch nicht)

^  ist xor.

a xor b     das ergebnis ist 1, wenn entweder a oder b, aber nicht
            beide 1 sind


    a    b    ergebnis
   ---------------------
    0    0       0
    1    0       1
    0    1       1
    1    1       0


a sei bei dir der Portpin, b ist das eine Bit in der Maske, welches 
logischerweise 1 ist.
D.h. aus der Tabelle sind nur die beiden Zeilen interessant, in denen b 
auf 1 ist. Und man erkennt:
  wenn a gleich 0 ist, dann ist das Ergebnis 1
  wenn a gleich 1 ist, dann ist das Ergebnis 0

D.h. das ganze läuft darauf hinaus, dass der Portpin bei jedem Ausführen 
von

    PORTB ^= (1<<PB5);

seinen Zustand wechselt. Ist der Pin vorher auf 0, dann ist er danach 
auf 1. Ist er vor der Operation auf 1, dann ist er danach auf 0.
Eben: er wird ge-toggelt.

von Moritz M. (moritzm)


Lesenswert?

Das ist eine Bitweise XOR-Verknüpfung:

Die Logiktabelle von XOR sieht so aus

0 0 = 0
0 1 = 1
1 0 = 1
1 1 = 0

Fall LED ist an:

PORTB sieht so aus:
0bxx1xxxxx
XOR-Verknüpft mit
0bxx1xxxxx
________
0bxx0xxxxx

Ergebnis: LED ist aus


Fall LED ist aus:

PORTB sieht so aus:
0bxx0xxxxx
XOR-Verknüpft mit
0bxx1xxxxx
________
0bxx1xxxxx

Ergebnis: LED ist an

Schönen Abend noch

edit: och da war jemand schneller ;-)

von Digit-22 N. (digit-22)


Lesenswert?

AAAAhhh

Das ist ja genial (^ Xor)
Jetzt verstehe ich es.

Mir ging es zwar hauptsächlich dadrum den Timer kennen zu lernen, aber 
es ist immer wieder schön mehr als nur einen AHA Effekt zu haben.

Feine Sache...



Danke Jungs. Ich werde jetzt erstmal noch ein paar Übungen mit Timer 
machen und wenn alles gut geht, geht es mit PWM los.

Mein Ziel ist ja in endeffekt mit einem externen 50 Hz PWM (RC Bereich) 
z.B. LED ein und aus zu schalten. Oder über die eine Fernsteuerung die 
Bilkfreuqenzen zu regeln....

Naja denke da werden noch einige Fragen von mir kommen.



LG
Shabi

von Tickzähler (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Für eine kleine Einführung in Timer, die die wichtigsten Dinge aus der
> praktischen Sicht heraus beleuchtet:
> FAQ: Timer

Auschnitt aus der FAQ Timer:
"... so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so 
schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also 
1 Mio Zählschritte."

... hmm, wenn ich mich nicht irre ist das ein Tick zu viel. Vom 0 bis 
1000000 sind es 1000001 Zählschritte.

von Cyblord -. (cyblord)


Lesenswert?

Tickzähler schrieb:
> Karl Heinz Buchegger schrieb:
>> Für eine kleine Einführung in Timer, die die wichtigsten Dinge aus der
>> praktischen Sicht heraus beleuchtet:
>> FAQ: Timer
>
> Auschnitt aus der FAQ Timer:
> "... so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so
> schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also
> 1 Mio Zählschritte."
>
> ... hmm, wenn ich mich nicht irre ist das ein Tick zu viel. Vom 0 bis
> 1000000 sind es 1000001 Zählschritte.

Tja ja das stimmt. Das alte Problem mit den null basierten Werten.
Man soll ja auch schon Programmierer gesehen haben, die drücken im 
Aufzug die 3 wenn sie in den 4. Stock wollen.

gruß cyblord

von Karl H. (kbuchegg)


Lesenswert?

Tickzähler schrieb:

> Auschnitt aus der FAQ Timer:
> "... so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so
> schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also
> 1 Mio Zählschritte."
>
> ... hmm, wenn ich mich nicht irre ist das ein Tick zu viel. Vom 0 bis
> 1000000 sind es 1000001 Zählschritte.

Ja, aber.

Angenommen der Zähler steht auf 0.
Jetzt macht er 1 Zählschritt. Auf welcher Zahl steht er dann. Auf 1.
Nach 2 Zählschritten steht er auf ... 2
Nach 3, auf 3 ....

Er durchläuft 1000001 verschiedene Zahlen, aber er macht dazu 1000000 
Zählvorgänge. Denn die 0 hat er ja quasi umsonst, ohne Zählschritt 
erhalten (und erst dann, wenn er von 1000000 wieder auf 0 zurückfällt, 
macht er dann den 1Mio+1-ten Zählschritt, der ihn wieder auf 0 
zurückbringt.

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.