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