Forum: Mikrocontroller und Digitale Elektronik ATmega328p Timer/Interrupt


von Matthias H. (h3inz3lmann)


Lesenswert?

Hi,

Ich bin neu hier im Forum und auf dem Gebiet der Mikrocontroller.
Ein paar erfahrungen hab ich schon mit Arduinos gesammelt und wollte 
jetzt mal mehr machen.
Zum Beispiel einen Timer zu Programmieren (klein anfangen und so..).
Der ATmega ist auf einem Arduino Nano drauf.
Das Programm sieht so aus:

#define F_CPU 16E6
#include <avr/io.h>
#include <avr/interrupt.h>

#define RED  (1 << 7)
#define YELLOW (1 << 6)
#define GREEN (1 << 5)
#define BLUE (1 << 4)

volatile uint8_t counter = 0;
int main(void) {
    DDRD |= RED | YELLOW | GREEN | BLUE;
    TCCR0A |= (1 << CS02);
    sei();
    while (1) {
        if (counter == 255) {
            PORTD ^= (RED | YELLOW | GREEN | BLUE);
            counter = 0;
        }
    }

}

ISR(TIMER0_OVF_vect){
    counter++;
}

An dem Pins ist jeweils eine LED dran, die Schaltung hat keine Fehler 
(hab sie vorher mit delays ja schön laufen gehabt).
Aber die LEDS bleiben einfach dunkel.
Kann mir jemand sagen (wo der wahrscheinlich dumme aus unverständnis 
entstandene Fehler) liegt oder mir einen Tipp geben, was ich beachten 
muss, damit es klappt?
Grüße Matthias

: Bearbeitet durch User
von Stefan D. (ste_d)


Lesenswert?

Moin,

Du musst das Timer-Interrupt noch aktivieren:
TIMSK0 |= (1<<TOIE0)

(siehe Seite 109 des Datenblatts)

Gruß, Stefan

von Matthias H. (h3inz3lmann)


Lesenswert?

Hi,

danke für dir schnelle Antwort.
Wieder was dazu gelernt, aber wohl noch nicht genug.
Das Register ist gesetzt, aber die LEDs gehen nicht an.
Im Datenblatt auf Seite 109 steht aber auch noch etwas vom Status 
Register, dessen I-Bit gesetzt werden muss, was ja so geschieht(denke 
ich):
SREG |= (1 << 7);

oder passiert das durch den Aufruf:
sei();
im Code?

Gruß Matthias

von Thomas E. (thomase)


Lesenswert?

Matthias H. schrieb:
> oder passiert das durch den Aufruf:
> sei();
> im Code?

Ja.

Aber dein Timer läuft nicht. Die CS-Bits werden im TCCR0B-Register 
gesetzt.

mfg.

von Matthias H. (h3inz3lmann)


Lesenswert?

Jetzt leuchtet es!=) Danke.
Aber nochmal zum Verständnis:
TCCR0 besteht aus 2 Registern TCCR0A und TCCR0B.
-TCCR0A ist der "der Timer" selbst, also das Register das inkrementiert 
wird
-TCCR0B ist der Prescaler
Stimmt das so?
Wo das Ding jetzt so hübsch blinkt hab ich aber noch eine weiter 
Verständnisfrage(anderes Thema, ich stelle sie der bequemlichkeit halber 
aber einfach hier):
Ich habe ja oben im Code die Angabe F_CPU 16E6,
Heißt das, das der AVR mit 16.000.000 Hertz läuft?(Ich habe versucht 
diese angabe zu verstehen, aber empfinde alle beschreibungen bisher als 
sehr Kryptisch)

Gruß Matthias

von Thomas E. (thomase)


Lesenswert?

Matthias H. schrieb:
> Jetzt leuchtet es!=) Danke.
> Aber nochmal zum Verständnis:
> TCCR0 besteht aus 2 Registern TCCR0A und TCCR0B.
> -TCCR0A ist der "der Timer" selbst, also das Register das inkrementiert
> wird
> -TCCR0B ist der Prescaler
> Stimmt das so?

Der Timer ist Timer0. Das ist ein Stück Hardware im Controller. Diese 
Hardware wird mit einem Satz von Registern eingestellt. Welche das sind 
und was damit eingestellt wird, findest du im Datenblatt unter "Register 
Description".

> Wo das Ding jetzt so hübsch blinkt hab ich aber noch eine weiter
> Verständnisfrage(anderes Thema, ich stelle sie der bequemlichkeit halber
> aber einfach hier):
> Ich habe ja oben im Code die Angabe F_CPU 16E6,
> Heißt das, das der AVR mit 16.000.000 Hertz läuft?(Ich habe versucht
> diese angabe zu verstehen, aber empfinde alle beschreibungen bisher als
> sehr Kryptisch)

Der Takt ist Bestandteil der Hardware. Also wird auch die Frequenz durch 
die Hardware bestimmt. Im Falle deines Arduino dürfte es so sein, dass 
der Controller per Fuses auf "externer Quarz" eingestellt und ein Quarz 
mit 16MHz angeschlossen ist.

Eingestellt wird mit F_CPU gar nichts. Die Angabe dient dazu, dem 
Compiler mitzuteien, dass der Controller mit eben dieser Frequenz läuft. 
Das wird benötigt um z.B. Timerkonfigurationen, Baudraten etc. mit 
Makros automatisch auszurechnen. Oder _delay_ms() zu konfigurieren.

mfg.

von Matthias H. (h3inz3lmann)


Lesenswert?

Stimmt, danke für die gute Erklärung nochmal.
Ich war nur wegen der Blink-frequnz etwas verwirrt.
aber es gilt ja
Taktfrequnz/Prescaler <=> 16.000.000/256 = 62500
also 62500 Inkrementierungen pro Sekunde
und damit
62500/256 = 244
Timer-Overflows pro Sekunde
das heißt, ich inkrementiere den counter 244 mal die Sekunde und daraus 
resultiert eine Frequenz von etwas mehr als einer Sekunde für die LEDs 
oder?

von Thomas E. (thomase)


Lesenswert?

Richtig. So wird das berechnet.

mfg.

von Matthias H. (h3inz3lmann)


Lesenswert?

Ok, danke für deine Hilfe!
hier jetzt nochmal zum Abschluss der korrigierte Code(ich wäre froh 
gewesen um den Thread und ein funktionierendes Codebeispiel :D)
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#define RED  (1 << 7)
5
#define YELLOW (1 << 6)
6
#define GREEN (1 << 5)
7
#define BLUE (1 << 4)
8
9
volatile uint8_t counter = 0;
10
int main(void) {
11
    DDRD |= RED | YELLOW | GREEN | BLUE;
12
    TCCR0B |= (1 << CS02);      //Prescaler von 256 für den Timer0
13
    sei();
14
    while (1) {
15
        if (counter == 244) {   // LEDs werden jede Sekunde getogglet
16
            PORTD ^= (RED | YELLOW | GREEN | BLUE);
17
            counter = 0;
18
        }
19
    }
20
21
}
22
23
ISR(TIMER0_OVF_vect){
24
    counter++;
25
}

von M. K. (sylaina)


Lesenswert?

Matthias H. schrieb:
> -TCCR0A ist der "der Timer" selbst, also das Register das inkrementiert
> wird
> -TCCR0B ist der Prescaler
> Stimmt das so?

Öhm, nein. TCCR0A und TCCR0B sind die Controll-Register, damit wird der 
Timer0 konfiguriert. Das Register, das inkrementiert wird, nennt sich 
TCNT0. Das steht aber auch im Datenblatt beim entsprechenden Timer, hier 
Timer0

von Thomas W. (Gast)


Lesenswert?

Matthias H. schrieb:
> Ich habe ja oben im Code die Angabe F_CPU 16E6,
> Heißt das, das der AVR mit 16.000.000 Hertz läuft?

Die Angabe "16E6" für die Taktfrequenz ist ausgesprochen unübliche, 
m.a.W. hier sehe ich das zum ersten Mal. Diese Expnentialdarstellung ist 
eher für Float Zahlen mit großem Dynamikbereich vorgesehen. Und 
messtechnisch betrachtet, sollte die Stellenanzahl in der 
Zahlendarstellung alle signifikanten Stellen enthalten.

Üblich ist die Angabe als vorzeichenlose Ganzzahl mit ausreichender 
Bitzahl, also in diesem Fall als unsigned long
1
#define F_CPU 16000000UL

von Thomas E. (thomase)


Lesenswert?

Thomas W. schrieb:
> Üblich ist die Angabe als vorzeichenlose Ganzzahl mit ausreichender
> Bitzahl, also in diesem Fall als unsigned long
1
#define F_CPU 16000000UL


Üblich ist die Angabe des Taktes in den Projekteinstellungen. Ein 
"define" im Quelltext ist eine weit verbreitete Unsitte.

mfg.

: Bearbeitet durch User
von M. K. (sylaina)


Lesenswert?

Thomas E. schrieb:
> Üblich ist die Angabe des Taktes in den Projekteinstellungen. Ein
> "define" im Quelltext ist eine weit verbreitete Unsitte.

Warum soll das eine Unsitte sein?

von Thomas E. (thomase)


Lesenswert?

Michael K. schrieb:
> Warum soll das eine Unsitte sein?

Falsche Frage.

Warum ist das eine Unsitte?

Weil ein Projekt i.a.R. aus mehreren Files besteht und die Einstellung 
dann in allen Files gemacht werden muss. Steht sie in den 
Projekteinstellungen, muss sie nur einmal gemacht werden und gilt für 
alle Files. Und auch nur für dieses Projekt. Benutzt man einzelne Files 
in mehreren Projekten, würde eine Änderung der defines sich u.U. auch in 
anderen Projekten auswirken.

mfg.

: Bearbeitet durch User
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.