Forum: Compiler & IDEs Timer beim ATmega 168 verstehn


von Peter (Gast)


Lesenswert?

Hallo

Erstmal möchte ich anmerken dass ich anfänger in uController 
programmierng bin.
Mit Hilfe des Totorial is es mir schon gelunden eine led auf meinem 
board blinken zu lassen, allesdings nur mit dem Wait befehl.

Jetz möchte ich dass ganze mit Timer realisieren...leider bis jetzt ohne 
erfolg.
Ich habe mir schon von der ATMEL Page die Docu geholt. Ehrlich gesagt 
check ich nicht wirklich viel.
Kann mir jemand erklähren wie dieser 8 Bit Timer/Counter funktioniert 
und wie ich eine LED zum blinken bringen kann?
Da ich vor einigen jahren mal c gelernt habe würd ich es vorziehen in C 
zu programmieren



Danke für Eure Antworten
Peter

von Karl H. (kbuchegg)


Lesenswert?

Im Grunde ist das ziemlich einfach.
Ein Timer ist einfach nur ein Stück Hardware. Ein Zähler
der vor sich hinzählt.

Wie schnell dieser Zähler zählt wird mit dem Vorteiler
eingestellt. Je nach eingestelltem Vorteiler wird der
Systemtakt (also die Quarzfrequenz) durch genau diesen
Faktor dividiert und mit dieser runtergesetzten Frequenz
zählt dann der Timer. In dem Moment wo du einen Vorteiler
setzt, zählt der Timer auch schon. Setzt du den Vorteiler
auf 0 hört der Timer wieder auf zu zählen.

Was kann man nun mit dem Timer machen?
(Ohne Anspruch auf Vollständigkeit)

Zuallererst kann man natürlich auf das Zählregister zugreifen.
Super. Aber meist nicht sehr sinnvoll. Timer werden ja
eingesetzt um eben nicht in einer Schleife Wartezeit zu
verbrutzeln. Wenn ich aber in einer Schleife ständig das
Zählregister überprüfe und warte bis das einen gewissen Wert
erreicht hat, mach ich doch genau dasselbe.
Das kanns also nicht sein.

Aber Timer haben noch ein paar andere nette Features.
Einer davon ist der Overflow Interrupt. Was'n das?
Overflow: Jeder Timer hat nur eine bestimmte Bitgröße.
Zb. Ist der Timer0 ein 8 Bit Timer. Das heist er zählt von
0 bis 255 (255 ist die größte Zahl, die mit 8 Bit gerade noch
darstellbar ist). Und dann? Dann passiert ein Überlauf (engl.
overflow) und der Timer fängt wieder bei 0 an. Das Interessante
an der ganzen Sache ist nun, dass dieser Overflow einen Interrupt
auslösen kann.
Was'n ein Interrupt?
Ein Interrupt ist eine 'Unterbrechung'. Das heist der µC lässt
alles stehen und liegen womit er gerade beschäftigt ist und
führt eine spezielle Funktion aus. Ist er mit der Abarbeitung
dieser Funktion fertig, dann nimmt er seine Arbeit wieder dort
auf, wo er unterbrochen wurde.

Die Kombination aus beidem bewirkt jetzt, dass man bei einem
Overflow des Timers eine bestimmte Funktion ausführen lassen
kann. Das passiert immer, egal womit der µC gerade beschäftigt
ist. Mit 2 Einschränkungen:
* Die Interrupts müssen generell freigegeben sein
* Eine Interrupt Funktion kann selbst nicht durch einen
  Interrupt unterbrochen werden. Die Funktion wird zuende gebracht
  und erst danach wird der neue Interrupt bearbeitet. (Das stimmt
  nicht ganz, aber fürs Erste belassen wir es mal dabei).

Soweit die Einführung.
Wie sieht das jetzt konkret in C, am Beispiel WinAvr avr-gcc aus?
Zb. So

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

ISR( TIMER0_OVF_vect )
{
  PORTB ^= 0xFF;
}

int main()
{
  DDRB = 0xFF;
  PORTB = 0x00;

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

  while( 1 ) {
  }
}

Die Programmausführung beginnt, wie immer bei main().
Dort richte ich zunächst mal einen Port her um dort
eine Ausgabe machen zu können. Ich nehm einfach mal
den kompletten Port als Ausgabe (DDRB = 0xFF;) und
geh davon aus, dass zumindest an einigen davon eine
LED angeschlossen ist.

  TCCR0 = ( 1 << CS01 ) | ( 1 << CS00 );

Hier wird der Vorteiler des Timers gesetzt. Aus dem
Datenblatt ist ersichtlich, dass ein Setzen der Bits
CS01 und CS00 den Systemtakt durch 64 teilt. Durch
setzen von anderen Bits könnte man auch andere Teilfaktoren
erreichen. Nachdem der Teiler gesetzt ist, beginnt der
Timer auch schon fröhlich vor sich hin zu tuckern.

  TIMSK = ( 1 << TOIE0 );

Das hier setzt das Bit, dass für den Overflow Interrupt
zuständig ist. Sobald also am Timer ein Overflow auftritt,
wird ein Interrupt ausgelöst. Der macht aber noch nichts,
da die Interrupts generell noch nicht freigegeben sind.

  sei();

behebt dieses (sei = Set Enable Iterrupt). Ab jetzt ist
alles scharf und ein Overflow würde einen Interrupt auslösen
den der µC auch mit einer speziellen Funktion, dem sog.
Interrupt Handler, abarbeiten wird.

In main() bleibt nichts mehr zu tun, also schicken wir den
µC in eine Schleife, in der er bis zum St. Nimmerleinstag
hängt:

  while( 1 ) {
  }

Soweit so gut.
Tritt jetzt am Timer ein Overflow ein, so wird eine speziell
Funktion, der Interrupt Handler aufgerufen.
Hier ist er vereinbart:

ISR( TIMER0_OVF_vect )
{

Das ISR ist obligatorisch, und sagt dem Compiler, dass das hier
keine normale Funktion, sondern ein Interrupt Handler ist.
Das TIMER0_OVF_vect sagt ihm auch noch, welcher Interrupt Handler
gemeint ist. Ein AVR kann viele verschiedene Interrupts auslösen,
wir wollen aber den Overflow des Timers 0 haben.

Und in dem Handler?

{
  PORTB ^= 0xFF;
}

Da schalte ich einfach den Ausgang vom PORTB um. Wenn an einem
Pin bisher eine 0 war, dann wird eine 1 daraus und umgekehrt.
Eine angeschlossene LED wird also entweder ein oder aus
geschaltet, bei jedem Aufruf des Interrupt Handlers immer
anders rum.

Und das wars dann auch schon.
Wie schnell blinkt nun die LED.
Lass uns mal rechnen.
Sagen wir mal dein µC arbeitet mit 1 Mhz. Wie schnell
zählt dann der Timer? Der Vorteiler ist momentan auf 64,
dh. der Timer zählt mit einer Frequenz von
 1000000 / 64 = 15652 Hz
Um einen Ocerflow auszulösen, muss der Timer einmal von 0
bis 255 zählen, also 256 Zählschritte ausführen. Ergo

   15652 / 256 = 61,irgendwas Hz

Mit dieser Frequenz wird die Led ein und ausgeschaltet.
DIe Flackerfrequenz beträgt also 61 / 2, also knapp 30 Hz

Deine Led flackert also bei 1 Mhz Taktfrequenz mit knapp 30 Hz
bei einem Vorteiler von 64. Das wirst du nicht sehen können.
Aber mann kann ja auch einen größeren Vorteiler nehmen :-)

von peter (Gast)


Lesenswert?

Hallo Karl Heinz

Danke erstmal für die Ausführliche Antwort. Das Hilft mir schon sehr 
weiter.
Wenn man als Anfänger in die Thematik reinkommen will ist das ziemlich 
mühsehlich. Hab gestern 5 Stunden die Atmel Referenz studiert bin aber 
auf keinen grünen zweig gekommen.

Mein letzendlches ziel soll eine Frequenzmessung eines rechtecksignals 
sein. Ich hab gesehn das der 16 Bit counter eine möglichkeit bietet den 
zähler mittels steigernder flanke zu starten und zu stoppen hast du da 
auch einen Tip für mich?


mfg

von johnny.m (Gast)


Lesenswert?

Du meinst wahrscheinlich den Capture-Modus. Dabei wird der Zähler aber 
nicht gestartet oder gestoppt, sondern er läuft durch und bei den 
entsprechenden Flanken am Capture-Eingang wird der aktuelle Stand des 
Zählers in das Capture-Register kopiert. Da dies hardwaremäßig 
geschieht, also ohne Verzögerung durch eventuell gerade ablaufende 
Prozesse, ist das eine sehr präzise Methode. Zu dem Thema gab es hier 
schon eine ganze Reihe Threads. Gib mal "Capture" und/oder 
"Frequenzmessung" in die Betreffsuche ein, da wirst Du geholfen...

von johnny.m (Gast)


Lesenswert?

BTW: Bei der Suche am besten im µC/Elektronik-Forum suchen. Da gibts 
erheblich mehr Beispiele. Und natürlich auch mal in der Codesammlung 
schauen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Input capture eignet sich aber letztlich nur (sinnvoll) zur Messung
eher kleiner Frequenzen.  Große Frequenzen misst man, indem man sie
extern in einen Timer (der dann eher zum Counter wird) einspeist
und mit einem zweiten Timer (Referenzzeit) dann den ersten startet
und anhält.  Die Anzahl der gezählten Impulse ist dann proporzional
zur Frequenz.

von Guest (Gast)


Lesenswert?

Hallo,

habe noch eine Frage zu diesem Thema. Habe den Timer wie oben 
beschrieben realisieren können, der Quarz den ich benutzt habe hatte 
allerdings 16Mhz, bei dem größten Vorteiler komme ich dann auf eine 
Frequenz von ca. 60 Hz.
Das ist mir aber noch zu groß, mit welcher Methode teilt ihr eure 
Frequenz noch weiter runter? Da gibt es bestimmt mehrere Möglichkeiten, 
brauche nur einen kleinen Schlag auf den Hinterkopf.
Danke

von Karl H. (kbuchegg)


Lesenswert?

Guest wrote:
> Hallo,
>
> habe noch eine Frage zu diesem Thema. Habe den Timer wie oben
> beschrieben realisieren können, der Quarz den ich benutzt habe hatte
> allerdings 16Mhz, bei dem größten Vorteiler komme ich dann auf eine
> Frequenz von ca. 60 Hz.
> Das ist mir aber noch zu groß, mit welcher Methode teilt ihr eure
> Frequenz noch weiter runter? Da gibt es bestimmt mehrere Möglichkeiten,
> brauche nur einen kleinen Schlag auf den Hinterkopf.

<klap>

Nichts und niemand hindert dich daran, selbst nochmal
einen Zähler in die ISR einzubauen und deine LED-Umschalte
nur dann zu machen, wenn die ISR zum, sagen wir mal, 5-ten
mal aufgerufen wurde.


Reicht der Klaps oder brauchst du's mit der groben Keule?  :-)

von Guest (Gast)


Lesenswert?

da ist wohl was dran, dachte man müßte in den allgemeinen 
Timer-Einstellungen den ISR einfach noch seltener auslösen lassen oder 
so.
Vielen Dank

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.