Moin,
Vorweg: Ich bin ganz Neu auf dem Gebiet und kenne mich nicht wirklich
aus. Ich habe gesehen das es massenhaft Threads zu dem Thema gibt, aber
irgendwie hilft mir das nicht weiter...
Ich habe hier ein STK600, mit einem ATMEGA2560 darauf, hab alles
verbunden und kann das ding Programmieren, LED´s nach belieben mit
Tastern schalten hat auch schon geklappt :D
Meine Ziele sind recht klein... Was ich vor habe:
Im Hauptprogramm leuchten alle LED´s, durch eine ISR soll nurnoch die
Hälfte der LED´s leuchten (für eine kurze Zeit)... Ich versuche mich so
langsam an die Timer/PWM geschichte ran zu tasten.
Mein Code bisher:
#include <avr/io.h>
#define F_CPU 1000000UL // CPU Takt auf 1MHz
#include <util/delay.h>
#include <avr/interrupt.h>
int main(void)
{
DDRA = 0xFF; // Port A = Ausgang
DDRC = 0x00; // Port C = Eingang
TCCR0A = (1<<CS02); //Prescaler des Timers = 256 =>
CPUTakt/256=3906
TIMSK0 |= (1<<TOIE0); //Overflow interrupt erlauben
sei(); //Globale Interrupts aktivieren
while (1)
{
PORTA = 0x00; // Alle LED´s leuchten
}
}
ISR (TIMER0_OVF_vect)
{
//Interrupt aktion, alle (1000000/256)/256 Hz = 15Hz
PORTA = 0x0F; //Hälfte der LED´s Leuchtet
_delay_ms(500); // 0,5s leuchtdauer
}
Kann mir jemand sagen warum das nicht klappt? :) Es leuchten einfach
permanent alle LED´s... :x
Liebe Grüße und Danke im Voraus!
Lasse ... schrieb:> Kann mir jemand sagen warum das nicht klappt? :) Es leuchten einfach> permanent alle LED´s... :x
Wird die ISR überhaupt aufgerufen? Kenne mich mit Atmega's nicht sehr
gut aus, bin eher auf PIC's... Allerdings ist es bei der IDE MPLAB
Beispielsweise so, dass diese "delay_ms" funktion nur dann funktioniert,
wenn man in einem Define den CPU Takt angibt. Weiss nicht, ob das hier
auch nötig ist, ist aber durchaus denkbar.
Lasse ... schrieb:> _delay_ms(500); // 0,5s leuchtdauer
Delay's in einem Interrupt solltest du möglichst vermeiden. Hier mag das
kein Problem sein, wenn du aber später Projekte realisieren willst, die
etwas grösser sind wird das so nichts.
Bei vielen Applikationen gibt es verschiedene Interrupt Auslöser. Sei es
nun UART, Timer oder was auch immer. Oft laufen diese module alle
Parallel, somit sollte die ISR möglichst kurz gehalten werden um
unnötige Verzögerungen zu verhindern. Am besten nur das allernötigste.
Du könntest in der ISR Beispielsweise ein Bit setzen und dieses in
deiner Main abfragen. Wenn das Bit 1 ist, rufst du eine Funktion auf
welche nur die hälfte deiner LED's leuchten lässt, machst die 500ms
Delay und deaktivierst das Bit wieder.
Hoffe konnte etwas helfen
Gruss
Lasse ... schrieb:> Ich versuche mich so> langsam an die Timer/PWM geschichte ran zu tasten.
Dann mach das richtig und arbeite z.B. die Tutorials hier durch ;).
Lasse ... schrieb:> TCCR0A = (1<<CS02); //Prescaler des Timers = 256 =>> CPUTakt/256=3906
Der Kommentar ist definitiv falsch. Keine Ahnung was dein eigentliches
Ziel ist, aber mit dieser Konfiguration komme ich bei einem Takt von 1
MHz auf eine Interruptfrequenz von 15 Hz - so wie es auch in den
Kommentaren der ISR selbst beschrieben wird.
Lasse ... schrieb:> while (1)> {> PORTA = 0x00; // Alle LED´s leuchten> }
Damit schaltest du im Prinzip "ständig" (sofern nicht gerade die ISR
ausgeführt wird) die LEDs wieder ein, das ist dir klar? Das solltest du
über die Schleife packen, und das Ein- bzw. Ausschalten in der ISR
durchführen bzw. über ein entsprechendes Flag kommunizieren.
Lasse ... schrieb:> ISR (TIMER0_OVF_vect)> {> //Interrupt aktion, alle (1000000/256)/256 Hz = 15Hz> PORTA = 0x0F; //Hälfte der LED´s Leuchtet> _delay_ms(500); // 0,5s leuchtdauer> }
Das ist maximaler Blödsinn. Verzögerungen dieser Form haben in ISRs
absolut (!) nichts zu suchen. Bei einer solch großen Verzögerung (0,5
Sekunden) verpasst du etliche Timer Overflows und die ISR wird bei der
nächsten Gelegenheit sofort wieder ausgeführt, weil die entsprechenden
Bits gesetzt sind ... Wieso stellst du den Timer nicht einfach
langsamer, oder kümmerst dich um einen Prescaler in Software?
Überhaupt hast du nicht so recht beschrieben was dein eigentliches Ziel
ist: Wie lange sollen die LEDs jeweils leuchten bzw. ausgeschaltet sein?
Dann könnte man dir in Sachen Timereinstellungen und ISR auch konkret
helfen ...
Mit freundlichen Grüßen,
Karol Babioch
Lasse ... schrieb:> TCCR0A = (1<<CS02); //Prescaler des Timers = 256 =>
Das wird in TCCR0B eingestellt.
> _delay_ms(500); // 0,5s leuchtdauer
Das hat in der ISR gar nichts zu suchen.
mfg.
Lasse ... schrieb:> Kann mir jemand sagen warum das nicht klappt? :) Es leuchten einfach> permanent alle LED´s... :x
Wie wäre es wenn du deinen Timer auch mal startest ?
Über die anderen Fehler wollen wir gar nicht reden.
Hallo,
was ich auf Anhieb sehe: Die Timer-Initialisierung ist unvollständig,
könnte aber gehen, da alle Bits, die Du nicht schreibst vorbelegt sind
(meist mit '0'), habe ich jetzt aber nicht genau durchgesehen, schau da
erst mal selbst, Du willst es ja verstehen. Daß das _delay_ms() im
Interrupt nicht schön ist wurde ja schon gesagt. Dadurch entsteht auch
noch das Problem, daß der Interrupt nach den 500 ms schon lange wieder
ansteht, das heisst, direkt nach den 500 ms wird der Interrupt sofort
wieder aufgerufen. Wenn der Timer also macht, was er soll, wirst Du von
dem Ablauf der main() nicht mehr viel sehen, er werden also (fast) immer
die Hälfte der LEDs leuchten.
Mit freundlichem Gruß - Martin
Ja ich hab das ganze mal komplett umgestellt und mit dem CTC Modus
gemacht, das habe ich so hinbekommen wie ich wollte :) Danke für die
Antworten!
Und generell habe ich im Interrupt nurnoch eine Variable die
inkrementiert wird und sobald die nen gewissen Wert hat, schalte ich die
LED´s so wie ichs dann eben möchte. Funktioniert auch :D
Hab mich dabei an dem Tutorial hier entlanggehangelt.
Eine Frage habe ich aber dennoch.
Im Moment sieht es so aus:
#include <avr/io.h>
#define F_CPU 6000000UL
#include <util/delay.h>
#include <avr/interrupt.h>
volatile unsigned int millisekunden=0;
volatile unsigned int Sekunden=0;
int main(void)
{
DDRA = 0xFF;
TCCR0A = (1<<WGM02); //CTC Modus
TCCR0B |= (1<<CS01); //Prescaler auf 8
OCR0A = 125-1;
TIMSK0 |= (1<<OCIE0A); //Compare Interrupt erlauben
sei(); //Globale Interrupts aktivieren
... irgen ein code in dem ich die led´s anwähle...
dann die ISR:
ISR (TIMER0_COMPA_vect)
{
millisekunden++;
if (millisekunden == 1000)
{
Sekunden++;
millisekunden=0;
if (Sekunden == 13)
{
Sekunden = 0;
}
}
}
Da ist oben dieses "OCR0A = 125-1;" Das soll ja den Comparewert
darstellen wenn ich das richtig verstanden habe. Also der Zähler, zählt
bis OCR0A und dann kommt der Interrupt.
Heißt das dann bei 6MHz clck: 6000000/Prescale/OCR0A = Zähltakt? Denn
wenn ich den Wert für OCR0A ändere, ändert sich nichts an der
Frequenz... (Die LED´s steuere ich der Reihe nach, quasi als lauflicht
an... da sollte man ja eine Geschwindigkeitsänderung feststellen können)
Hallo,
dieses mal ist es nur eine Kleinigkeit: in TCCR0A must Du WGM01 setzen,
und nicht wie jetzt WGM02, um in den CTC-Modus zu kommen. Das Bit WGM02
ist übrigens im Register TCCR0B. So bist Du nicht im CTC-Modus, der
Timer läuft immer bis 255, und die Änderung von OCR0A bewirkt keine
Veränderung der Geschwindigkeit.
Mit freundlichem Gruß - Martin
Hi,
ich würde dir gerne noch ein paar Tipps mit den auf den Weg geben, da
mir beim Durchgehen deines Quelltexts noch das ein oder andere
aufgefallen ist.
Lasse ... schrieb:> #define F_CPU 6000000UL
Das wird i.d.R. im Makefile bzw. von der IDE festgelegt.
Lasse ... schrieb:> volatile unsigned int millisekunden=0;> volatile unsigned int Sekunden=0;
Es ist eine gute Idee Variablen und den gesamten Quellcode Englisch zu
halten. Wer weiß wer das Ganze nachher mal lesen soll, und zumindest ich
zucke im ersten Moment zusammen, wenn ich da was Deutsches sehe ;).
Überhaupt sollte man konsequent mit der Benennung sein - auch mit der
Groß- bzw. Kleinschreibung. Variablen benennt man i.d.R. klein, Makros
groß. Zur Worttrennung benutzt man entweder CamelCase oder Underscores
(_).
Lasse ... schrieb:> TCCR0A = (1<<WGM02); //CTC Modus> TCCR0B |= (1<<CS01); //Prescaler auf 8
AUch hier fehlt mir die Konsequenz. Warum das eine mal eine reine
Zuweisung (=) und das andere Mal eine Veroderung (|=). Sofern du nicht
vorher schon an den Registern herum gefuscht hast, kannst du Werte
wirklich direkt zuordnen. Damit schließt du auch Seiteneffekte aus, die
sich durch Umbauarbeiten ergeben könnten, wenn du doch mal vorher etwas
am Register ändern solltest.
Lasse ... schrieb:> OCR0A = 125-1;
Das ist eine sog. "magische" Konstante, und diese sind sehr unschön.
In deinem Fall sogar komplett ohne Kommentar, sodass man nur "raten"
kann wo dieser Wert herkommt. Bei einer Änderung musst du im Quellcode
herum fummeln - ggf. sogar an mehreren Stellen. All das ist
fehleranfällig und unnötig. Definiere lieber gut kommentierte Makros und
überlasse dem Compiler die Rechnerei.
Lasse ... schrieb:> Also der Zähler, zählt> bis OCR0A und dann kommt der Interrupt.
Ja, wobei streng genommen nicht direkt der Interrupt ausgeführt wird,
sondern nur das entsprechende Flag in TIFR0 gesetzt wird. Nur sofern die
Interrupts global aktiviert sind (sei()), kein anderer Interrupt
ausgeführt wird bzw. kein anderer höher priorisierter Interrupt ansteht,
wird die dazugehörige ISR angesprungen. In deinem Fall spielt das alles
natürlich nur eine untergeordnete Rolle, solltest du dir aber vor Augen
führen, sobald du mal mit mehreren Interruptquellen arbeitest.
Mit freundlichen Grüßen,
Karol Babioch
Karol Babioch schrieb:> Lasse ... schrieb:>> #define F_CPU 6000000UL>> Das wird i.d.R. im Makefile bzw. von der IDE festgelegt.
Das wird mit den Fuses im Controller und ggf. einem entsprechenden Quarz
festgelegt und dem Compiler mitgeteilt.
Ich glaube nämlich, unser Freund irrt hier gewaltig:
Lasse ... schrieb:> #define F_CPU 1000000UL // CPU Takt auf 1MHz
mfg.