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
intmain(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
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
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.
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
> 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.
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.
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
volatileintZaehler=0;
8
9
intmain(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
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.
//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
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.
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.
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.
...die nächste mögliche Fehlerquelle, sobald du den richtigen Interrupt
freigibst: Nutzt das Arduino-Framework evtl. selbst schon einen Timer?
MfG, Arno
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.
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.
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.
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.
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
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
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.
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 ;)