Hallo, Ich habe jetzt mehrere Themen gelesen, jedoch ist keines auf meinem "Niveau" :/ Habe hier einen mega8515. Ich kann bislang lediglich mit dem Controler Ein- und Ausgänge und einfach Schleifen realisieren. Ich würde gernen eine LED einfach nur blinken lassen im Takt von vll. einer Sek. Kann mir wer detailliert erklären wie ich welches Timer register zu setzten habe? Darüber würde ich mich wirklich sehr freuen. Gruß Jannik
Schreib doch schon mal die Source soweit hin, wie du kommst. Und vergiss nicht anzugeben, mit welchem Takt dein Atmega8515 in deiner Schaltung betrieben wird.
Hallo Stefan, Leider bin ich noch garnicht weit, da ich hier im gcc Tut einfach nicht durchblicke welche Register ich jetzt wirklich brauche. Quasi als Minimalkonfiguration! Das einzige was ich jetzt einigermaßen verstehe, ist das ich nach jedem überlaufen von meinem 16Bit Register einen Interrupt hervorrufe, mit dem ich dann z.B. auch einen Ausgang setzten kann?! Damit dies nicht zu schnell geschieht setzte ich z.B. TCCR1B = 0b00000101 womit ich dann die F_CPU / 1024 teilen würde?! und mit sei() aktiviere ich meine Interrupts. Das sind wie du merkst alles nur Bruchteile. Ich finde auch kein vernünftiges Beispiel wo es wirklich für Anfänger kommentiert ist. Meine F_CPU ist bei 8 Mhz.
Schreib ein Gerüst ohne das ganze Timer-Gedöns. Also ein einfaches Beispiel wie du es zum LED-Blinken brauchst. Du kannst auch schon deine Überlegungen zum Timer einfügen und mit Kommentar // Unfertig markieren.
Hallo, was Du außer dem Tutorial immer zur Hand haben mußt, ist das komplette Datenblatt des jeweiligen AVR. Dort dann eben nach 16 Bit Timer schauen, interessiere Dich für Dein Vorhaben vorrangig für die Betriebsart CTC (Clear Timer on Compare Match). Such die nötigen Register und Werte zusammen und versuch Dein Glück. Das Programm kannst Du dann ja gern hier zur Diskussion und Fehlersuchhilfe hier reinstellen. Gruß aus Berlin Michael
1 | #include <avr/io.h> |
2 | |
3 | int main(void) |
4 | {
|
5 | DDRA = 0xff; //Ausgang LCD |
6 | DDRB = 0xff; //Ausgang LED |
7 | DDRC = 0x00; //Eingang DIP |
8 | DDRD = 0x00; //Eingang DIP |
9 | |
10 | //Vermutlich hier die ungewisse Konfiguration der Timer-Register
|
11 | |
12 | if (bit_is_set(PINC, 2)) { |
13 | PORTB = 0xff; //PortB soll im Takt von etwa 1ner Sek blinken |
14 | }
|
15 | /*
|
16 | Das wars zunächst. Das dient für mich erstmal nur zum reinen Verständnis
|
17 | |
18 | */
|
19 | |
20 | }
|
JannikS wrote: > > Das einzige was ich jetzt einigermaßen verstehe, ist das ich nach jedem > überlaufen von meinem 16Bit Register einen Interrupt hervorrufe, mit dem > ich dann z.B. auch einen Ausgang setzten kann?! > Damit dies nicht zu schnell geschieht setzte ich z.B. TCCR1B = > 0b00000101 womit ich dann die F_CPU / 1024 teilen würde?! und mit sei() > aktiviere ich meine Interrupts. > > Das sind wie du merkst alles nur Bruchteile. Nö, das ist eigentlich schon fast alles, was es über Timer in dieser Betriebsart zu sagen gibt. Also: schnapp dir das Datenblatt. Such dir die Register raus. Such dir die Bits in den Registern raus, die du setzen musst und das wars dann auch schon fast. > Meine F_CPU ist bei 8 Mhz. OK. Ist schon mal was. Zur Rumrechnerei mit dem Vorteiler kannst du dir ja mal das hier reinziehen: http://www.mikrocontroller.net/articles/AVR-Tutorial:_Timer Tja. Und dann heist es: Einfach mal ein Programm schreiben. Im Simulator durchprobieren, ob alle Registerwerte so gesetzt sind, wie sie es sein sollen und ob deine ISR auch wirklich aufgerufen wird. Hilft alles nichts. Da musst du durch und ins kalte Wasser springen. Aber keine Angst, das ist keine Raketentechnik und im Grunde genommen ziemlich simpel.
1/ Wieviele LEDs hast du an PORTB, weil du den kompletten Port veränderst? Es würde bei einer LED reichen, einen Pin vom Port zu verändern. 2/ Bedeutet PORTB = 0xff; die LEDs sind AN oder sind AUS? Wenn es eine Schaltung nach STK500 Muster ist, wären mit dieser Anweisung die LED(s) alle AUS, weil diese aktiv-low beschaltet sind. Hier ist ein Kommentar im Sourcecode hilfreich. 3/ So könnte es aussehen:
1 | //
|
2 | // Demo Timer - JannikS
|
3 | // uC = Atmega8515
|
4 | // F_CPU = 8 MHz
|
5 | //
|
6 | |
7 | #include <avr/io.h> |
8 | #include <avr/interrupt.h> // Hilfsdefintionen für Interrupts |
9 | |
10 | int main(void) |
11 | {
|
12 | DDRB = 0xff; // Ausgang LED |
13 | |
14 | /**
|
15 | Datenblatt: http://www.atmel.com/dyn/resources/prod_documents/doc2512.pdf
|
16 | Ab Seite 80: Timer0
|
17 | |
18 | Als am einfachsten verstehbarer Modus wird der Normal Mode (Seite 85)
|
19 | von Timer0 gewählt:
|
20 |
|
21 | Ein 8-Bit Register zahlt von einem Anfangswert (Voreinstelltung 0)
|
22 | bis 0xFF hoch. Wenn 0xFF erreicht ist, führt die nächste Erhöhung um
|
23 | Eins zu einem Ergebnis von 0 (neuer Startwert zum Hochzählen) und zu
|
24 | dem gewünschten Overflow-Interrupt
|
25 |
|
26 | Wie das Register TCCR0 (Timer/Counter Control Register) zu setzen ist,
|
27 | um den Normal Mode einzustellen steht in Table 44 bei den Bits WGM01
|
28 | und WGM02
|
29 | |
30 | Eine weitere Einstellmöglichkeit ist, wie schnell hochgezählt werden
|
31 | soll. Das kann mit der vollen Taktrate F_CPU geschehen oder indem man
|
32 | die Taktrate durch einen Vorteiler (Prescaler) teilt. Die möglichen
|
33 | Prescaler 1, 8, 64, 256, 1024 sind in Table 48 angegeben.
|
34 |
|
35 | Berechnung der Zeit bis zum Overflow bei verschiedenen Prescaler
|
36 | Einstellungen
|
37 |
|
38 | Overflow = 1/F_CPU * 256
|
39 |
|
40 | Prescaler Overflow
|
41 | 1 32 us
|
42 | 8 256 us
|
43 | 64 2048 us
|
44 | 256 8192 us
|
45 | 1024 32768 us = 32,768 ms
|
46 |
|
47 | 1s sind aber 1000 ms bzw. 1000000 us...
|
48 | d.h. man braucht mehrere Overflow Interrupts in denen die nur Anzahl
|
49 | der Interrupts gezählt wird und erst wenn die Anzahl mal die Dauer
|
50 | die gewünschte Sekunde ergibt, wird die LED umgeschaltet.
|
51 | |
52 | Wieviele Overflows braucht man für eine Sekunde bei verschiedenen
|
53 | Prescaler Werten?
|
54 |
|
55 | Anzahl_Overflows = 1s / Zeit_für_einen_Overflow
|
56 | |
57 | Prescaler Overflow Anzahl_Overflows
|
58 | 1 32 us 31250
|
59 | 8 256 us 3906,25
|
60 | 64 2048 us 488,28125
|
61 | 256 8192 us 122,0703125
|
62 | 1024 32768 us 30,517578125
|
63 |
|
64 | Bzw. die Gesamtrechnung ist:
|
65 | |
66 | Anzahl_Overflows = 1 * F_CPU / 256
|
67 | |
68 | Hier ist der Wert bei Prescaler 1 interessant: Die Anzahl der nötigen
|
69 | ist Overflows ist nämlich eine ganze Zahl und das vereinfacht das
|
70 | Programmieren!
|
71 | |
72 | Dieser Wert wird im Timer0-Interrupt verwendet.
|
73 | */
|
74 | |
75 | // 8-Bit TIMER0 Normal Mode, Prescaler 1
|
76 | TCCR0 = (0 << WGM01) | (0 << WGM00) |
77 | | (0 << CS02) | (0 << CS01) | (1 << CS00) ; |
78 | |
79 | // Und den spezellen Timer0 Overflow Interrupt nicht nur konfigurieren,
|
80 | // sondern auch zulassen (enablen) d.h. das Timer/Counter Interrupt Mask
|
81 | // Register setzen (Seite 93)
|
82 | |
83 | TIMSK = (1 << TOIE0); |
84 | |
85 | // Alle zugelassenen Interrupts einschalten
|
86 | sei(); |
87 | |
88 | // Endlosschleife
|
89 | // 1. main() nie verlassen
|
90 | // 2. wird vom Timer0 Overflow Interrupt unterbrochen
|
91 | while(1) |
92 | {
|
93 | }
|
94 | }
|
95 | |
96 | /**
|
97 | Dies ist die Interruptroutine, die aufgerufen wird,
|
98 | wenn der eingestellte Timer einen Überlauf hat.
|
99 | |
100 | Der Name ist aus Datenblatt und aus avr/iom8515.h
|
101 | ersichtlich. avr/iom8515.h ist die genaue Beschreibung
|
102 | deines µC und wird über avr/io.h eingebunden.
|
103 | |
104 | Ein möglicher Interruptvektor steht dort im Abschnitt:
|
105 | |
106 | // Timer/Counter0 Overflow
|
107 | #define TIMER0_OVF_vect _VECTOR(7)
|
108 | |
109 | Timer0 ist ein 8-Bit-Timer mit Overflow Modus
|
110 | */
|
111 | |
112 | unsigned int Anzahl_Overflows; |
113 | |
114 | ISR(TIMER0_OVF_vect) |
115 | {
|
116 | Anzahl_Overflows++; |
117 | |
118 | // Prüfen ob 1s voll
|
119 | if (Anzahl_Overflows == 1 * F_CPU / 256) |
120 | {
|
121 | // LOW/HIGH mit Hilfe der XOR Bitmanipulation umschalten
|
122 | // das schaltet die LED(s) AN/AUS
|
123 | |
124 | // In der Simulation auf die nächste Zeile einen Breakpoint setzen (F9)
|
125 | // und das Programm voll laufen lassen (F5).
|
126 | // Dann mit Hilfe der Stop Watch im I/O View die Zeit bis zum
|
127 | // nächsten Erreichen des Breakpoints messen.
|
128 | // (Nicht ungeduldig werden. Das dauert auf meinem Rechner ca. 65 s)
|
129 | PORTB ^= 0xFF; |
130 | |
131 | // Zurücksetzen für nächste 1s Wartezeit
|
132 | Anzahl_Overflows = 0; |
133 | }
|
134 | }
|
> 3/ So könnte es aussehen:
Der Code ist bei weitem nicht optimal, sondern soll nur eine erste Idee
geben (die Scheu vor Interrupts nehmen).
Die Nachteile sind u.a. das grobe Missverhältnis von Arbeitszeit in dem
Hauptprogramm (zu wenig, hier stört es zum Glück nicht) zur Arbeitszeit
in der Interruptroutine (zu viel). Sowie die Platzverschwendung mit der
Hilfsvariable Anzahl_Overflows. Beides kann man geschickter (aber
vielleicht weniger einsichtig) machen.
Wow, vielen Dank Stefan für diese ausführliche Erklärung. Ich werde mich dann mal im detail damit beschäftigen!
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.