Moinmoin, Ich bin auf der suche nach einem möglichst vollständigen Beispielcode für einen 16-Bit-Timer im AtMega8. Also konkret in main() den Timer vorbereiten und dann wie die Interruptroutine aussieht. Wie hält man Timer in der Interruptroutine wieder an, Wie weist man neuen Zählerwert zu und wie lässt man den dann weiterlaufen. Ich möchte zuerst nur eine LED in definierten Abständen blinken lassen oder einen Ton an einem Digitalport ausgeben, um die Verwendung erstmal besser zu verinnerlichen. Jan
Im Tutorial steht aber leider kein brauchbarer Beispielcode :( Jan
Da hat er recht :-) Ist aber trivial. Wenn du den Rest verstanden hast, dann sollte das kein Problem sein. Aber was solls.
1 | #define F_CPU 8000000UL
|
2 | |
3 | #include <stdio.h> |
4 | #include <avr\io.h> |
5 | #include <avr\interrupt.h> |
6 | |
7 | ISR( TIMER0_OVF_vect ) |
8 | {
|
9 | PORTB ^= 0xFF; |
10 | }
|
11 | |
12 | int main() |
13 | {
|
14 | DDRB = 0xFF; |
15 | PORTB = 0x00; |
16 | |
17 | TCCR0 = ( 1 << CS01 ) | ( 1 << CS00 ); |
18 | TIMSK = ( 1 << TOIE0 ); |
19 | sei(); |
20 | |
21 | while( 1 ) { |
22 | }
|
23 | }
|
Das mag jetzt vielleicht bei dir etwas schnell blinken. Da musst du dann halt zusätzlich im Interrupt noch etwas runterteilen.
1 | #define F_CPU 8000000UL
|
2 | |
3 | #include <stdio.h> |
4 | #include <avr\io.h> |
5 | #include <avr\interrupt.h> |
6 | |
7 | int Teiler; |
8 | |
9 | ISR( TIMER0_OVF_vect ) |
10 | {
|
11 | Teiler++; |
12 | if( Teiler == 100 ) { |
13 | Teiler = 0; |
14 | PORTB ^= 0xFF; |
15 | }
|
16 | }
|
17 | |
18 | int main() |
19 | {
|
20 | DDRB = 0xFF; |
21 | PORTB = 0x00; |
22 | |
23 | Teiler = 0; |
24 | |
25 | TCCR0 = ( 1 << CS01 ) | ( 1 << CS00 ); |
26 | TIMSK = ( 1 << TOIE0 ); |
27 | sei(); |
28 | |
29 | while( 1 ) { |
30 | }
|
31 | }
|
Teiler++; if( Teiler >= 100 ) { Das wäre dann perfekt :-)
Hallo Karl Heinz, Vielen Dank für den Code! 1. Leider ist der Code zum Realisieren von einer Melodie an einem Pieper über einen Digital-out noch nicht ganz vollständig, deshalb habe ich noch eine kleine Nachfrage. 2. Ist der von dir genannte Code nicht ein Beispielcode für den 8-Bit Counter oder sehe ich das falsch? also erstmal zur Nachfrage: Wie halte ich innerhalb der Serviceroutine den 16-Bit Counter an, weise einen neuen Zählerstand zu und lasse den dann weiterlaufen. Des weiteren muss ich innerhalb der ISR (Interrupt Service Routine) zusätzlich die Interrupts deaktivieren, damit kein (wie nennt sich das nochmal) overflow stattfindet. (Also kein neuer Interrupt obwohl der alte noch nicht abgearbeitet ist.)
>Des weiteren muss ich innerhalb der ISR (Interrupt Service Routine) >zusätzlich die Interrupts deaktivieren, damit kein (wie nennt sich das >nochmal) overflow stattfindet. (Also kein neuer Interrupt obwohl der >alte noch nicht abgearbeitet ist.) Nein, das macht der AVR für dich. Dafür gibt es keine Prioritäten zwischen den Interrupts, die dafür sorgen könnten, dass ein Interrupt mit höherer Priorität einen mit niedriegerer unterbrechen kann (vgl. 8051).
@ 1001. Rahul Achso. Naja aber trotzdem muss man wissen, wie man in den 16-Bit-Timer innerhalb der 16-Bit-Timer-Interruptroutine eine neue Zählhöhe reinschreibt, um wie schon erwähnt z.B. eine Melodie an einem Digitalen Portpin auszugeben. Da liegt momentan mein Problem. Außerdem halte ich es für Sinnvoll, dass dieser Beispielcode, wenn er fertig ist und eine Melodie (z.B. Alle meine Entchen) ausgeben kann auch in das Tutorial hier reinkommt weil das einfach Basics sind, die da stumpf fehlen, obwohl das Tutorial immer so hoch gelobt wird.
>Achso. Naja aber trotzdem muss man wissen, wie man in den 16-Bit-Timer >innerhalb der 16-Bit-Timer-Interruptroutine eine neue Zählhöhe >reinschreibt, So wie du es auch ausserhalb des int machst, vielleicht?
>Naja aber trotzdem muss man wissen
TCNT = neuer Wert; // und fertig.
Für sowas sollte man aber eher einen PWM-Mode benutzen...
Wie sieht das mit den interrupts denn nun eigentlich aus... Wenn sich dir CPU beispielsweise innerhalt von interrupt vom timer befindet, was passiert dann wenn ein Interrupt von einem Digitaleingang kommt? Garnicht? Wird der Interrupt komplett ignoriert? Oder wird er in eine "warteschleife" gesetzt? Ich habe nur erfahrungen mit einem 8051 und da ist das offensichtlich komplett anders.
>da ist das offensichtlich komplett anders. Da liegst du richtig. Hier auf der Seite (oben links bei den Links) gibt es den Abschnitt "AVR", dort gibt es Tutorien, die sich mit der Einführung zum AVR befassen. Das sollte eine Anlaufstelle für die sein, genauso wie der erste Teil des Datenblattes deines Controller.
Das Tutorial ist mir bekannt. Aber ein Beispiel für die benutzung des 16-Bit Timers gibt es da trotzdem nicht. 8-Bit ist kein Problem. Das steht da, aber 16-Bit ebend nicht und da der 16-Bit Timer nen ganzes Stück komplizierter ist, ist das Tutorial so wie es jetzt ist für den 16-Bit Counter völlig unbrauchbar, weil das was bisher zum 16-Bit Timer im Tutorial steht deutlich ausführlicher im Datenblatt steht. Aber auch das was im Datenblatt steht ist noch kein brauchbares Beispiel. Was daher in so ein Tutorial sollte, damit es sich vom Datenblatt unterscheidet, ist ein vernünftiges Beispiel für den 16-Bit counter. Und genau das versuche ich momentan (leider bisher mit wenig Erfolg) zu schreiben.
>da der 16-Bit Timer nen ganzes Stück komplizierter ist Wo das denn? >Was daher in so ein Tutorial sollte, damit es sich vom Datenblatt >unterscheidet, ist ein vernünftiges Beispiel für den 16-Bit counter. Dann weisst du ja, was du demnächst machen wirst... >Und genau das versuche ich momentan (leider bisher mit wenig Erfolg) zu >schreiben. Es gibt also schon Code. Sowas postet man hier und lässt es dann von "uns" und anderen Experten zerpflücken bis man keine Lust mehr auf controllern oder dieses Forum hat ;-)
@Rahul Okay. Dann sei hiermit mein bisheriger Code zur Diskussion freigegeben ;) #define F_CPU 8000000UL #include <stdlib.h> #include <avr/io.h> #include <avr/interrupt.h> //#include <util/delay.h> // ab avr-libc Version 1.2.0 möglich und empfohlen: #include <stdint.h> // veraltet: #include <inttypes.h> ISR( TIMER1_OVF_vect ) { PORTD ^= 0xFF; } int main (void) { //Portkonfiguration DDRD = 0b11111111; // Datenrichtung des Ports Bit im Register gesetzt //76543210 // (1) für Ausgang, Bit gelöscht // (0) für Eingang. Als Ausgang 0=GND 1=VCC PORTD = 0b11100000; // Ausgang der letzten 5 Bits wird auf Masse gezogen (LED's eingeschaltet) //16 Bit Timer // Die Bits: COM1a1 COM1a0 NA NA NA NA PWM11 PWM10 TCCR1A = 0b00000000; //PWM11 und PWM10 = 0 => PWM-Betriebsart nicht aktiviert - Timer1 arbeitet als normaler Timer/Zähler //COM1a1 und COM1a0 = 0 => OC1 wird nicht angesteuert while(1) { //Hier in Sleepmodus setzen so dass Interrupts aufwecken } /* wird nie erreicht */ return 0; }
Die AVRs besitzen einen CTC-Modus, bei dem das Nachladen des Timers mit einem Startwert entfällt. Der ist für solche Sachen wesentlich besser geeignet, als der Overflow...
Und was ist mit TIMSK? Ein SEI sehe ich auch nirgends...
Und den Timer startest du auch nirgends... Guck dir am besten mal die Registerbeschreibungen des Timer1 im Datenblatt an.
Übrigens verhält sich der 16-Bit-Timer bei der Initialisierung (und dem Überlauf-Betrieb) meistens genauso wie ein 8-Bit-Timer...
>kranker Jan
Wenn es eine Grippe ist, solltest du die vielleicht lieber erst mal
loswerden...
Was ist SEI ? Aktuell siehts jetzt so aus: ISR( TIMER1_OVF_vect ) { PORTD ^= 0xFF; //PORTD = ~PORTD; } int main (void) { //Portkonfiguration DDRD = 0b11111111; // Datenrichtung des Ports Bit im Register gesetzt //76543210 // (1) für Ausgang, Bit gelöscht // (0) für Eingang. Als Ausgang 0=GND 1=VCC PORTD = 0b11100000; // Ausgang der letzten 5 Bits wird auf Masse gezogen (LED's eingeschaltet) //16 Bit Timer // Die Bits: COM1a1 COM1a0 NA NA NA NA PWM11 PWM10 TCCR1A = 0b00000000; //PWM11 und PWM10 = 0 => PWM-Betriebsart nicht aktiviert - Timer1 arbeitet als normaler Timer/Zähler //COM1a1 und COM1a0 = 0 => OC1 wird nicht angesteuert // Die Bits: ICNC1 ICES1 NA NA CTC1 CS12 CS11 CS10 // CTC1 CS12 CS11 // 0 0 0 angehalten // 0 0 1 CPU-Takt // 0 1 0 1/8 // 0 1 1 1/64 // 1 0 0 1/256 // 1 0 1 1/1024 TCCR1B = 0b00000011; // Die Bits: TOIE1 OCIE1A NA NA TICIE NA TOIE0 NA TIMSK = 0b10000000; //TOIE1 = 1 => Timer Overflow 1 Interrupt wird ausgelöst while(1) { //Hier in Sleepmodus setzen so dass Interrupts aufwecken } /* wird nie erreicht */ return 0; } BTW: Wie postet man Quelltext so schön formatiert wie Heinz das gemacht hat?
Das heißt "sei()" und nicht "SEI". Bitte bitte bitte lies das Tutorial!!! Da steht wirklich alles drin!
> Wie postet man Quelltext so schön formatiert wie Heinz das gemacht > hat? OK, das steht nicht im Tutorial. Das geht mit "eckige Klammer auf C eckige Klammer zu" und "eckige Klammer auf /C eckige Klammer zu"...
Test <c> #define F_CPU 8000000UL #include <stdlib.h> #include <avr/io.h> #include <avr/interrupt.h> //#include <util/delay.h> // ab avr-libc Version 1.2.0 möglich und empfohlen: #include <stdint.h> // veraltet: #include <inttypes.h> ISR( TIMER1_OVF_vect ) { //PORTD ^= 0xFF; PORTD = ~PORTD; } int main (void) { //Portkonfiguration DDRD = 0b11111111; // Datenrichtung des Ports Bit im Register gesetzt //76543210 // (1) für Ausgang, Bit gelöscht // (0) für Eingang. Als Ausgang 0=GND 1=VCC PORTD = 0b11100000; // Ausgang der letzten 5 Bits wird auf Masse gezogen (LED's eingeschaltet) //16 Bit Timer // Die Bits: COM1a1 COM1a0 NA NA NA NA PWM11 PWM10 TCCR1A = 0b00000000; //PWM11 und PWM10 = 0 => PWM-Betriebsart nicht aktiviert - Timer1 arbeitet als normaler Timer/Zähler //COM1a1 und COM1a0 = 0 => OC1 wird nicht angesteuert // Die Bits: ICNC1 ICES1 NA NA CTC1 CS12 CS11 CS10 // CTC1 CS12 CS11 // 0 0 0 angehalten // 0 0 1 CPU-Takt // 0 1 0 1/8 // 0 1 1 1/64 // 1 0 0 1/256 // 1 0 1 1/1024 TCCR1B = 0b00000011; // Die Bits: TOIE1 OCIE1A NA NA TICIE NA TOIE0 NA TIMSK = 0b10000000; //TOIE1 = 1 => Timer Overflow 1 Interrupt wird ausgelöst sei(); while(1) { //Hier in Sleepmodus setzen so dass Interrupts aufwecken } /* wird nie erreicht */ return 0; } </c>
1 | #define F_CPU 8000000UL
|
2 | |
3 | #include <stdlib.h> |
4 | #include <avr/io.h> |
5 | #include <avr/interrupt.h> |
6 | |
7 | //#include <util/delay.h>
|
8 | |
9 | // ab avr-libc Version 1.2.0 möglich und empfohlen:
|
10 | #include <stdint.h> |
11 | // veraltet: #include <inttypes.h>
|
12 | |
13 | |
14 | ISR( TIMER1_OVF_vect ) |
15 | {
|
16 | //PORTD ^= 0xFF;
|
17 | PORTD = ~PORTD; |
18 | }
|
19 | |
20 | |
21 | int main (void) { |
22 | |
23 | //Portkonfiguration
|
24 | |
25 | DDRD = 0b11111111; // Datenrichtung des Ports Bit im Register gesetzt |
26 | //76543210 // (1) für Ausgang, Bit gelöscht
|
27 | // (0) für Eingang. Als Ausgang 0=GND 1=VCC
|
28 | |
29 | PORTD = 0b11100000; // Ausgang der letzten 5 Bits wird auf Masse gezogen (LED's eingeschaltet) |
30 | |
31 | |
32 | //16 Bit Timer
|
33 | // Die Bits: COM1a1 COM1a0 NA NA NA NA PWM11 PWM10
|
34 | TCCR1A = 0b00000000; |
35 | //PWM11 und PWM10 = 0 => PWM-Betriebsart nicht aktiviert - Timer1 arbeitet als normaler Timer/Zähler
|
36 | //COM1a1 und COM1a0 = 0 => OC1 wird nicht angesteuert
|
37 | |
38 | // Die Bits: ICNC1 ICES1 NA NA CTC1 CS12 CS11 CS10
|
39 | // CTC1 CS12 CS11
|
40 | // 0 0 0 angehalten
|
41 | // 0 0 1 CPU-Takt
|
42 | // 0 1 0 1/8
|
43 | // 0 1 1 1/64
|
44 | // 1 0 0 1/256
|
45 | // 1 0 1 1/1024
|
46 | TCCR1B = 0b00000011; |
47 | |
48 | |
49 | // Die Bits: TOIE1 OCIE1A NA NA TICIE NA TOIE0 NA
|
50 | TIMSK = 0b10000000; //TOIE1 = 1 => Timer Overflow 1 Interrupt wird ausgelöst |
51 | |
52 | sei(); |
53 | |
54 | while(1) { |
55 | //Hier in Sleepmodus setzen so dass Interrupts aufwecken
|
56 | }
|
57 | |
58 | /* wird nie erreicht */
|
59 | return 0; |
60 | }
|
Warum ziehst du dann nicht das unnütze Test-Posting zurück? Du darfst das nämlich (für deine Postings). Außerdem hättest du es nach Feststellung des Nicht-Funktionierens noch passend editieren können, statt noch eins hinterher zu werfen.
Du willst also einen Melodiegenerator bauen. Sag das doch gleich. Also: Für sowas würde ich 2 Timer benutzen. Timer1, der 16 Bit Timer, wird im CTC Modus betrieben. Seine Aufgabe ist es das Tonsignal zu erzeugen, indem ein Pin getogeglt wird. CTC: Clear Timer on Compare. Schau dir das unbedingt im Datenblatt an! Das Prinzip ist Folgendes: Ein Timer zählt ja durch bis er seinen Endstand erreicht hat. Dann kann er einen Interrupt auslösen und fängt wieder bei 0 an. Beim normalen Timerbetrieb ist der Endstand vorgegeben und er Interrupt ist der 'Overflow-Interrupt'. Im CTC Modus kannst du aber den gewünschten Endstand vorgeben und der Interrupt ist auch ein anderer. Steht aber alles im Datenblatt. Der Rest ist ein bioschen rumrechnerei: Welches muss der Timer Endstand sein, damit sich am Output Pin die richtige Frequenz ergibt. Soweit zum ersten Timer. Der zweite Timer ist dafür zuständig, dass die Endstände für den Timer1 in der richgtigen Reihenfolge gesetzt werden. Der läuft also durch und sorgt mit seinen Overflows für das Basistiming aus dem dann letztendendes die Länge der Achtel-, Viertel-, Halbe-, ... Noten und Pausen erzeugt werden. Im Prinizp kannst du da die Vorlage von oben nehmen und dich im Overflow Interrupt austoben. Einfach mitzählen wieviele Overflows das jetzt waren. Die abzuspielende Note gibt durch ihre Längenangabe vor, wieviele das sein müssen. Bei erreichen dieser Anzahl, wird die nächste NOte geholt, der Endstand für den Timer1 neu gesetzt und in einer globalen Variablen vermerkt wieviele Overflows der Timer0 wieder abwarten muss, bis die nächste Note drankommt. Aber erst mal klein anfangen. Ich würde mal damit anfangen, den Timer1 im CTC Modus zu betreiben und einen Ton hörbar zu machen. Die Tonhöhe kann durchaus im Programm zunächst mal ein fixer Wert wein, der inn das Compare-Register des Timer1 geschrieben wird. Alleine mit dem umprogrammieren dieses Compare-Wertes kann man schon eine Menge Spass haben: zb. In einer Schleife den Wert erhöhen und eine kurze Zeit warten; oder andersrum; oder nach einer Wartezeit von vielleicht 500ms Zufallszahlen reinschreiben, oder ...
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.