Forum: Mikrocontroller und Digitale Elektronik AVR Ardunino Nano ATMega328P Timer OVF (Overflow) Interrupt


von Alexander G. (aaalex)


Lesenswert?

Hallo,

ich bin noch relativ am Anfang mit der Programmierung in C. Ich benutze 
als Board den Arduino Nano mit dem ATMega328P und als 
Entwicklungsumgebung das AVR Studio 4.19 mit dem Avr-Gcc Compiler.

Bei meinem aktuellen Projekt beschäftige ich mich mit den Timern. Dazu 
versuche ich die onboard LED an PB5 über den Timer 0 Overflow Interrupt 
blinken zu lassen. Dabei soll eine andere onboard LED über ein delay 
blinken. Dazu mein Code:
1
#define F_CPU 16000000UL  // 16MHz
2
#include <avr/io.h>
3
#include <util/delay.h>
4
#include <avr/interrupt.h>
5
#define LED_L 5  // HIGH-active LED an PB5
6
#define LED_RX 0  // LOW-active LED an PD0
7
8
int main(void)
9
  {
10
  DDRD  |= (1 << LED_RX);  // Pin der LED_RX als Ausgang setzen
11
  PORTD |= (1 << LED_RX);  // LED_RX ausschalten
12
  DDRB  |= (1 << LED_L);  // Pin der LED_L als Ausgang setzen
13
  PORTB &= ~(1 << LED_L);  // LED_L ausschalen
14
  sei();  // globale Interrupts aktivieren
15
  TCCR0B = 0b00000011;  // Clock für Timer vom Prescaler auf 1/64
16
  TIMSK0 |= (1 << 1);  // TOIE0=1: Timer/Counter0 Overflow interrupt aktivieren
17
  while(1)
18
    {
19
    PORTD &= ~(1 << LED_RX);  // LED einschalten
20
    _delay_ms(500);           // 0,5s warten
21
    PORTD |= (1 << LED_RX);   // LED ausschalten
22
    _delay_ms(500);           // 0,5s warten
23
    }
24
  }
25
26
ISR(TIMER0_OVF_vect)
27
  {
28
  PORTB ^= (1 << LED_L);  // LED_L Zustand wechseln
29
  }


Folgendes ist mir bei der Fehlersuche aufgefallen:
    Die while-Schleife wird nicht einmal aufgerufen.
    Sobald eine der Zeilen 14, 15 oder 16 auskommentiere, wird 
wenigstens die while-Schleife ausgeführt, aber logischerweise nicht der 
Interrupt.
    Es funktioniert auch nicht mit dem Timer 1 oder Timer 2.
    Der Timer 0 funktioniert, da ich dessen Wert auf einem LCD 
ausgegeben habe, was einwandfrei funktionierte.

Bei meiner Internetrecherche fand ich nur Codes, die nichts anders 
gemacht haben als ich. Auch eine Kopie solcher Codes mit Anpassungen auf 
meine Hardware zeigten leider keinen Erfolg und ich habe mittlerweile 
auch keine Ideen mehr.

Hat jemand eine Idee wo mein Fehler liegt oder Anregungen, was ich noch 
überprüfen könnte?

Mit frendlichen Grüßen

von Jens M. (schuchkleisser)


Lesenswert?

2 Dinge fallen mir auf, bei beiden bin ich mir allerdings nicht sicher, 
aber das wäre ein Anhaltspunkt mal nachzusehen:
1. Dein Int kommt evtl. sehr schnell, so das nach der Freigabe sofort 
der Int kommt und beim Ende des Int steht schon der nächste an. So wird 
das while-Konstrukt nicht gestartet weil die Prozessorzeit vorher 
verdampft.
2. Du setzt das Intflag nicht zurück.
Ich weiß nicht ob der Compiler oder der Chip das machen, aber auch das 
würde (falls nicht zurückgesetzt) eine Endlosschleife durch den Int 
bauen.

So oder so: (wenn es denn klappt) sind 64 Teile von 16MHz zu schnell um 
die LED blinken zu sehen.

von Peter D. (peda)


Lesenswert?

Vermutlich der falsche Typ im Makefile, so daß der Interrupt in den Wald 
zeigt.

von Ingo E. (ogni42)


Lesenswert?

Hast Du den richtigen Controller in der Projektdatei ausgewählt?

Deine ISR wird auf jeden Fall zu schnell aufgerufen, um die LED blinken 
zu sehen:
16E6Hz/16/256 = 976,5625Hz

Du solltest den 1024 Prescaler verwenden -> 61 Hz und  dann mittels 
Zählvariable auf Deinen Blinktakt teilen

von S. Landolt (Gast)


Lesenswert?

> TIMSK0 |= (1 << 1);  // TOIE0=1: Timer/Counter0
> Overflow interrupt aktivieren

TOIE0 ist 0! - Sprung nach '__bad_interrupt'.

Und wie Jens M. schrieb: selbst mit /1024 wird es ein extrem schnelles 
Blinken.

von S. Landolt (Gast)


Lesenswert?

> bin noch relativ am Anfang mit der Programmierung
Dann also der Tipp:
TIMSK0 |= (1 << TOIE0);
hätte das Problem vermieden.

von Helfer (Gast)


Lesenswert?

S. Landolt schrieb:
> TOIE0 ist 0!

Gut aufgepasst!

Für den ambitionierten Anfänger: Nimmt man die die vorgefertigten
Bit-Namen dann passieren weniger Fehler.
1
  TIMSK0 |= (1 << TOIE0);  // TOIE0=1: Timer/Counter0

von Alexander G. (aaalex)


Lesenswert?

Peter D. schrieb:
> Vermutlich der falsche Typ im Makefile, so daß der Interrupt in den Wald
> zeigt.

Ich kann damit leider wenig anfangen. Ist gemeint, dass ich noch 
definieren muss, wo der Interrupt hinzeigt? Im Datenblatt steht zu dem 
Interrupt "TIMER0_OVF" die Program Adress "0x0020". Müsste ich das noch 
definieren? Wobei das eigentlich auch nicht sein kann, da ich in einem 
anderen Programm den "INT0_vect" ohne eine weitere Definition benutzen 
konnte und mit einem Taster einen Interrupt auslösen konnte.

Jens M. schrieb:
> 1. Dein Int kommt evtl. sehr schnell, so das nach der Freigabe sofort
> der Int kommt und beim Ende des Int steht schon der nächste an. So wird
> das while-Konstrukt nicht gestartet weil die Prozessorzeit vorher
> verdampft.

Das kann natürlich sein. Jedoch müsste hierbei die LED_L, die im 
Interrupt geschaltet wird blinken oder zumindest leuchten (durch das 
nicht wahrnehmbar schnelle blinken).

Jens M. schrieb:
> 2. Du setzt das Intflag nicht zurück.
> Ich weiß nicht ob der Compiler oder der Chip das machen, aber auch das
> würde (falls nicht zurückgesetzt) eine Endlosschleife durch den Int
> bauen.

Dies macht der Chip. Im Datenblatt steht: "The bit TOV0 is set when an 
overflow occurs in Timer/Counter0. TOV0 is cleared by hardware when 
executing the corresponding interrupt handling vector. Alternatively, 
TOV0 is cleared by writing a logic one to the flag. When the SREG I-bit, 
TOIE0 (Timer/Counter0 Overflow Interrupt Enable), and TOV0 are set, the 
Timer/Counter0 Overflow interrupt is executed."

Wenn ich es richtig verstehe, dann besagt der 2. Satz, dass nach dem 
Aufrufen des Interrupts das Flag zurückgesetzt wird. Desweiteren habe 
ich mal eine logische 1 auf den TOV0 Pin in der ISR geschrieben, was das 
Flag ebenfalls zurücksetzen sollte. Dies hat jedoch ebenfalls nicht 
funktioniert.

Außerdem habe ich den Clock nun auf 1/1024 des Prescalers gesetzt und 
einen Zähler eingebaut, der dafür sorgt, dass die LED im Sekundentakt 
blinkt. Jedoch beides ohne Erfolg
1
#define F_CPU 16000000UL  // 16MHz
2
#include <avr/io.h>
3
#include <util/delay.h>
4
#include <avr/interrupt.h>
5
#define LED_L 5  // HIGH-active LED an PB5
6
#define LED_RX 0  // LOW-active TX LED an PD0
7
volatile int Zaehler = 0;
8
9
int main(void)
10
  {
11
  DDRD  |= (1 << LED_RX);  // Pin der LED_RX als Ausgang setzen
12
  PORTD |= (1 << LED_RX);  // LED_RX ausschalten
13
  DDRB  |= (1 << LED_L);  // Pin der LED_L als Ausgang setzen
14
  PORTB &= ~(1 << LED_L);  // LED_L ausschalen
15
  //PORTB |= (1 << LED_L);  // LED einschalen
16
  //PRR &= ~(1 << 5);  // TC0 aktivieren
17
  sei();  // globale Interrupts aktivieren
18
  TCCR0B = 0b00000101;  // Clock für Timer vom Prescaler auf 1/1024
19
  TIMSK0 |= (1 << 1);  // TOIE0=1: Timer/Counter0 Overflow interrupt aktivieren
20
21
  while(1)
22
    {
23
    PORTD &= ~(1 << LED_RX);  // LED einschalten
24
    _delay_ms(500);           // 0,5s warten
25
    PORTD |= (1 << LED_RX);   // LED ausschalten
26
    _delay_ms(500);           // 0,5s warten
27
    }
28
  }
29
30
ISR(TIMER0_OVF_vect)
31
  {
32
  Zaehler++;
33
  if(Zaehler == 15625)  // Wenn 1s vergangen
34
    {
35
    PORTB ^= (1 << LED_L);  // LED Zustand wechseln
36
    }
37
  //TIFR0 |= (1 << 1);  // Interrupt Flag zurücksetzen
38
  }

von Helfer (Gast)


Lesenswert?

Alexander G. schrieb:
> Ich kann damit leider wenig anfangen.

PeDa meinte den falschen Controller-Typ der eventuell in deinen
Projekt eingetragen ist.

von Helfer (Gast)


Lesenswert?

Alexander G. schrieb:
> Jedoch beides ohne Erfolg

Lies alle Beiträge! Hier die Lösung:

S. Landolt schrieb:
> TOIE0 ist 0! - Sprung nach '__bad_interrupt'.

Wenn du den falschen Interrupt freigibst für den keine ISR
definiert ist dann fängt dein Controller bei jedem Interrupt
ganz von vorne an.

von S. Landolt (Gast)


Lesenswert?

Stefan Frings? Sind Sie das?

von Helfer (Gast)


Lesenswert?

S. Landolt schrieb:
> Sind Sie das?

Nein, S., ich bin ich und Stefan ist Stefan, er meldet sich
immer mit seinem "echten" Namen, nicht mit Nick.

von S. Landolt (Gast)


Lesenswert?

Dann nichts für ungut. Es schien mir nur so, wegen der ähnlichen Art und 
Ausdrucksweise.

von Ingo E. (ogni42)


Lesenswert?

1
  TIMSK0 |= (1 << 1);  // TOIE0=1: Timer/Counter0 Overflow interrupt aktivieren
Immer noch die falsche Maske
1
ISR(TIMER0_OVF_vect)
2
  {
3
  Zaehler++;
4
  if(Zaehler == 15625)  // Wenn 1s vergangen
5
    {
6
    PORTB ^= (1 << LED_L);  // LED Zustand wechseln
7
    }
8
  //TIFR0 |= (1 << 1);  // Interrupt Flag zurücksetzen
9
  }

TIFR muss nicht zurück gesetzt werden. Lies das bitte im DaBla nach.
Du musst Deinen Zähler auch zurück setzen, sonst schaltet die ISR genau 
einmal um und danach nie wieder

von Helfer (Gast)


Lesenswert?

S. Landolt schrieb:
> Dann nichts für ungut.

Passt schon. Ich bin zwar nicht so mit Helfersyndrom ausgestattet
wie Stefan, hatte aber das Bedürfnis einige flapsige Informationen
so mundgerecht aufzubereiten dass es der (offensichtliche) Anfänger
besser versteht. Sein letzter Beitrag wie auch darauf hin dass
er noch gewisse Anfänger-Probleme hat.

von Helfer (Gast)


Lesenswert?

Ingo E. schrieb:
> Lies das bitte im DaBla nach.

Lies du mal nach wie ein Zähler funktioniert. Er zählt einfach
weiter, sonst gäbe es keinen Overflow.

von Ingo E. (ogni42)


Lesenswert?

Helfer schrieb:
> Lies du mal nach wie ein Zähler funktioniert. Er zählt einfach
> weiter, sonst gäbe es keinen Overflow.

Dann schau Dir mal die ISR an, es geht um die Variable Zaehler, nicht 
den Timer.

Lesen, nachdenken, kommentieren.

von Jens M. (schuchkleisser)


Lesenswert?

Alexander G. schrieb:
> Jedoch müsste hierbei die LED_L, die im
> Interrupt geschaltet wird blinken oder zumindest leuchten

Wenn man es sehr schnell macht, evtl. auch nicht. Stichwort 
Read/Modify/Write mit Portpins.
Aber in Assembler bin ich eher bei PICs, da funktioniert manches etwas 
anders.
Trotzdem ein Punkt über den Anfänger oft fallen.

Alexander G. schrieb:
> Dies macht der Chip.

Ja, sorry. Bei PICs muss man's selber machen.

Aber das Problem wurde ja schon identifiziert: du setzt den falschen 
Int.
Das mit den komischen Masken mit geshifteten Bits ist aber auch ein 
Kram.
PICs können direkt ein Bit setzen ;) und man fällt nicht über &=, |= und 
^= mit >> und << und was weiß ich noch.

Und nicht vergessen: bei Überlauf deines Zählers musst du den auch 
zurücksetzen, sonst ist der erste Ablauf geplant kurz, die danach 
länger.

von Einer K. (Gast)


Lesenswert?

Ingo E. schrieb:
> if(Zaehler == 15625)  // Wenn 1s vergangen
Verrechnet, oder Kommentar falsch

Gefegt und ausgebessert
1
#define  F_CPU 16000000UL  // 16MHz
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
6
// Pin Masken
7
#define  LED_L  _BV(PB5)  // HIGH-active LED an PB5
8
#define  LED_RX _BV(PD0)  // LOW-active TX LED an PD0
9
10
11
volatile unsigned zaehler;
12
13
int main(void)
14
{
15
  DDRD  |=  LED_RX;  // Pin der LED_RX als Ausgang setzen
16
  PORTD |=  LED_RX;  // LED_RX ausschalten
17
  DDRB  |=  LED_L;   // Pin der LED_L als Ausgang setzen
18
  PORTB &= ~LED_L;   // LED_L ausschalen
19
20
  TCCR0B = _BV(CS02) | _BV(CS00); // Clock für Timer vom Prescaler auf 1/1024
21
  TIMSK0 = _BV(TOIE0);   // TOIE0=1: Timer/Counter0 Overflow interrupt aktivieren
22
  
23
  sei();                    // globale Interrupts aktivieren
24
  
25
  while (1)
26
  {
27
    PIND = LED_RX;    // LED toggle
28
    _delay_ms(500);   // 0,5s warten
29
  }
30
}
31
32
ISR(TIMER0_OVF_vect)
33
{
34
  zaehler++;
35
  if (zaehler >= 61) // Wenn 1s vergangen
36
  {
37
    zaehler = 0;
38
    PINB = LED_L;  // LED Zustand wechseln
39
  }
40
}

von Arno (Gast)


Lesenswert?

...die nächste mögliche Fehlerquelle, sobald du den richtigen Interrupt 
freigibst: Nutzt das Arduino-Framework evtl. selbst schon einen Timer?

MfG, Arno

von Einer K. (Gast)


Lesenswert?

Arno schrieb:
> ...die nächste mögliche Fehlerquelle, sobald du den richtigen Interrupt
> freigibst: Nutzt das Arduino-Framework evtl. selbst schon einen Timer?


Wenn eine main() angelegt wird, werden vom Arduino Framework keine Timer 
initialisiert.
Zudem nutzt er die Arduino IDE gar nicht.

Das vorherige Beispiel von mir, läuft in der Arduino IDE.
Und wirds auch wohl mit dem Atmel Studio tun.

von leo (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> volatile unsigned zaehler;

Das volatile ist wohl ueberfluessig, da zaehler ja nur in der ISR 
benutzt wird, oder?

leo

von Einer K. (Gast)


Lesenswert?

Schadet aber auch nicht.
Darum habe ich es auch so aus der Vorlage übernommen.
Wie so einiges andere auch....

von Jens M. (schuchkleisser)


Lesenswert?

Arduino Fanboy D. schrieb:
> PORTD |=  LED_RX;  // LED_RX ausschalten
> PORTB &= ~LED_L;   // LED_L ausschalen

Eins dieser Dinge ist nicht wie die anderen...

|= ist "bitweise oder", also schaltet |= die LED ein, nicht?
&= ist "bitwese und", ~ heißt "bitweise nicht", also &= ~ macht das 
angegebene Bit aus.

Ansonsten aber sehr vorbildlich und übersichtlich, auch von mir ein 
Danke.

von Einer K. (Gast)


Lesenswert?

Jens M. schrieb:
> Eins dieser Dinge ist nicht wie die anderen...
Ach, das passt schon.

Ihm sagte im Eingangsposting:
Alexander G. schrieb:
> #define LED_L 5  // HIGH-active LED an PB5
> #define LED_RX 0  // LOW-active LED an PD0

Also genau das!
Eine LED arbeitet invertiert, die andere nicht.

Ich würde das sicherlich anders lösen, habe mich allerdings auch da an 
die Vorlage gehalten.

Jens M. schrieb:
> Ansonsten aber sehr vorbildlich und übersichtlich, auch von mir ein
> Danke.
Danke, für die Blumen.

von Jens M. (schuchkleisser)


Lesenswert?

Ach, shifu.
Ja ok, das hab ich übersehen.

Ein Grund mehr für sowas Makros einzusetzen.
In 10000 Zeilen Code kommt sonst keiner mehr dahinter warum ein OR hier 
was an- und dort was ausmacht.

von Einer K. (Gast)


Lesenswert?

Jens M. schrieb:
> In 10000 Zeilen Code kommt sonst keiner mehr dahinter warum ein OR hier
> was an- und dort was ausmacht.

Ja, der TO hat noch einiges zu lernen!
Unter anderem das von dir genannte...
Oder auch wie giftig sich magische Zahlen auswirken (können).

Aber das kommt mit der Zeit/Erfahrung

von Arno (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Arno schrieb:
>> ...die nächste mögliche Fehlerquelle, sobald du den richtigen Interrupt
>> freigibst: Nutzt das Arduino-Framework evtl. selbst schon einen Timer?
>
> Wenn eine main() angelegt wird, werden vom Arduino Framework keine Timer
> initialisiert.
> Zudem nutzt er die Arduino IDE gar nicht.

Du hast Recht, ich hab mich da vorhin verguckt.

Sorry,
Heiko

von Einer K. (Gast)


Lesenswert?

Jens M. schrieb:
> Ja ok, das hab ich übersehen.

Arno schrieb:
> Du hast Recht, ich hab mich da vorhin verguckt.

Eine positive Bewertung von mir, an euch beide, wegen zugeben eines 
Irrtums.
Wäre eigentlich nicht unbedingt erwähnenswert.
.. aber in diesem Forum ist einiges "anders" ...

z.B. werde ich hier mit negativen Bewertungen belohnt.
Trotz fachlich korrekter Ansagen.
Schätze mal, dass diese anonymen Gestalten das Licht scheuen, lieber 
feige aus dem Hinterhalt schießen, als sich öffentlich zu stellen.


Das ist schon ok so...
Werde es mit Würde tragen.

von Alexander G. (aaalex)


Lesenswert?

S. Landolt schrieb:
> TOIE0 ist 0! - Sprung nach '__bad_interrupt'.

Das war der Fehler. Ich hatte einen prinzipiellen Denkfehler...
Also Vielen Dank für die helfende Unerstützung und die weiteren 
Hinweise, die noch genannt wurden ;)

Beitrag #6615510 wurde von einem Moderator gelöscht.
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.