Hi, ich habe zum Lernen und Verstehen des ATMega8's auf einem myAVR-Board ein Programm geschrieben, das die 3 Leuchtdioden binärmäßig sekundnweise schalten soll: #include <avr/io.h> #include <avr/signal.h> #include <avr/interrupt.h> #define TL 0xf7 #define TH 0xc2 unsigned char i=0; int main(void) { DDRD = 0x00; PORTD |= (1<<PD2) | (1<<PD3); DDRB = 0xff; PORTB = 0x00; TCCR1B |= (1<<CS00) | (1<<CS02); TCNT1L = TL; TCNT1H = TH; TIMSK |= (1<<TOIE1); TCNT1L = TL; TCNT1H = TH; sei(); while (1) {} return 0; } SIGNAL(SIG_OVERFLOW1) { i = PINB; i++; if (i==8) i=0; PORTB =i; TCNT1L = TL; TCNT1H = TH; } Meine Frage bezieht sich auf die Zeilen TCNT1L = TL; TCNT1H = TH; TIMSK |= (1<<TOIE1); TCNT1L = TL; TCNT1H = TH; Es kommt mir dämlich vor, zweimal die TCNT1x initialisieren zu müssen, ansonsten (egal, welche der beiden ich weglasse) zählt der Timer erstmal von FFFF runter, d.h. die erste LED geht (bei 16MHz) erst nach ca. 4s an. Mache ich was falsch? Gruß, Axel
Hi, der Teil außerhalb der while(1)-Schleife deines main-Programmes läuft ja nur einmal. Dort wird der Timer mit TL/TH vorgeladen und beim Setzen des Prescalers läuft der Timer los. Nach 1 Sekunde kommt der gewollte Überlauf. Zu diesem Zeitpunkt ist der aktuelle Zählerstand (TL/TH) = 0. Wenn du innerhalb der Überlauf-ISR (SIGNAL(OVERFLOW)) den Timer nicht wieder mit den Standardwerten lädst, zählt er keine Sekunde mehr sondern von 0...65535 (0...0xFFFF). In deinem Programm setzt du den Timer bei einem Überlauf immer wieder auf 0xC2F7 (49911). Von diesem Wert zählt der Timer dann wieder bis 0xFFFF (65535). Du wirst mir Recht geben, dass hier der Überlauf schneller kommt, als wenn er von 0 bis 65535 zählen müsste. Mfg Tommy
Sorry, war etwas zu schnell. Ich habe mir gerade nochmal deine Quelle angeschaut. Hast du das Programm selber geschrieben oder irgendwo abgetippt? Ich gehe immer wie folgt vor. (1) Timer stoppen (Prescaler ausschalten) (2) Zählerwert (TH/TL) setzen (3) Konfigurationen in den Registern setzen (4) Prescaler-Bits via "OR-Verknüpfung" setzen -> Timer wird gestoppt Also denke ich, dass du problemlos bei deinem Quelltext die letzten beiden Zeilen auskommentieren. TCNT1L = TL; TCNT1H = TH; TIMSK |= (1<<TOIE1); //TCNT1L = TL; //TCNT1H = TH; Also einfach mal probieren... Viele Grüße Tommy
Das klappt eben nicht -> wenn ich das so mache, zählt der Timer einmal bis FFFF bis die erste LED angeht, danach gehts korrekt im Sek-Takt weiter. Verstehen tue ich das auch nicht; aber vielleicht kann mir das jd. erklären. Gruß, Axel
Mal diverse Anmerkungen in wahlloser Reihenfolge.
1 | #define TL 0xf7
|
2 | #define TH 0xc2
|
3 | ...
|
4 | TCNT1L = TL; |
5 | TCNT1H = TH; |
Sorry, aber das würde ich als ,,Assemblerprogrammierung mit C-Syntax'' bezeichnen. (Selbst in Assembler kann man das besser formulieren.) Ich würde das so schreiben:
1 | #include <stdint.h> |
2 | |
3 | #include <avr/io.h> |
4 | |
5 | #define F_CPU 16000000ul
|
6 | |
7 | #define TMR1_PRESCALER 1024
|
8 | /* 1-second overflow preset value */
|
9 | #define TMR1_PRESET ((uint16_t)(-(F_CPU / TMR1_PRESCALER)))
|
10 | |
11 | void ioinit(void) |
12 | {
|
13 | ...
|
14 | TCNT1 = TMR1_PRESET; |
15 | }
|
1 | unsigned char i=0; |
Diese Variable sollte static innerhalb der ISR sein, denn nur dort wird sie gebraucht.
1 | TCCR1B |= (1<<CS00) | (1<<CS02); |
Schönheitsfehler: du setzt Timer 1, aber nimmst die Definitionsbits von Timer 0. Meine Variante:
1 | #include <stdint.h> |
2 | |
3 | #include <avr/interrupt.h> |
4 | #include <avr/io.h> |
5 | #include <avr/sleep.h> |
6 | |
7 | #define F_CPU 16000000ul
|
8 | |
9 | #define TMR1_PRESCALER 1024
|
10 | /* 1-second overflow preset value */
|
11 | #define TMR1_PRESET ((uint16_t)(-(F_CPU / TMR1_PRESCALER)))
|
12 | |
13 | void ioinit(void) |
14 | {
|
15 | /* Enable pullups */
|
16 | PORTD |= (1<<PD2) | (1<<PD3); |
17 | /* LED outputs */
|
18 | DDRB = 0xff; |
19 | |
20 | /* Prepare timer 1... */
|
21 | TCNT1 = TMR1_PRESET; |
22 | TIMSK |= (1<<TOIE1); |
23 | /* ...and kick it. */
|
24 | TCCR1B |= (1<<CS10) | (1<<CS12); |
25 | |
26 | sei(); |
27 | }
|
28 | |
29 | ISR(TIMER1_OVF_vect) |
30 | {
|
31 | uint8_t i; |
32 | |
33 | TCNT1 = TMR1_PRESET; |
34 | i = PORTB; |
35 | PORTB = (i & ~7) | (((i & 7) + 1) & 7); |
36 | }
|
37 | |
38 | int
|
39 | main(void) |
40 | {
|
41 | ioinit(); |
42 | for (;;) |
43 | sleep_mode(); |
44 | return 0; |
45 | }
|
(Für avr-libc < 1.4.x musst du ISR durch SIGNAL ersetzen und den alten Vektornamen benutzen.) Im Simulator blinkt zumindest die LED im Sekundentakt. Das Ganze hat natürlich rein akademischen Wert: in der Praxis würde ich statt der umständlichen und mit Jitter behafteten Methode des Overflow-Interrupts den CTC-Modus des Timers nehmen.
Hi, Danke; ich übe noch und bin für alle Tips sehr dankbar; ich werde es ausprobieren. Gruß, Axel
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.