Forum: Mikrocontroller und Digitale Elektronik LED´s in Interrupt für kurze Zeit leuchten lassen


von Lasse .. (lassem)


Lesenswert?

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!

von San L. (zwillingsfreunde)


Lesenswert?

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

von Karol B. (johnpatcher)


Lesenswert?

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
von Thomas E. (thomase)


Lesenswert?

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.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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.

von Martin S. (led_martin)


Lesenswert?

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

von Lasse .. (lassem)


Lesenswert?

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)

von Martin S. (led_martin)


Lesenswert?

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

von Lasse .. (lassem)


Lesenswert?

Ah cool!! Danke dir! Funktioniert :)

von Karol B. (johnpatcher)


Lesenswert?

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

von Thomas E. (thomase)


Lesenswert?

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
Noch kein Account? Hier anmelden.