Forum: Mikrocontroller und Digitale Elektronik LED blinkt während Programmablauf


von Cuadrado (Gast)


Lesenswert?

Hallo,

ich habe mir eben das Beispiel vom Timer und Zähler 
(http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR) 
durchgelesen, da ich eine LED die ganze Zeit während der Controller 
(ATmega8) läuft blinken lassen möchte, unabhängig vom Hauptprogramm.

Ist so etwas möglich? Ich habe in der Anleitung folgenden Quellcode 
gefunden, hierbei blinkt zwar immer meine LED, aber das Hauptprogramm 
lässt sich nicht mehr ausführen. Welche ISR benötige ich genau für so 
etwas?

#define F_CPU 1000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

int main(void)
{
  // Timer 0 konfigurieren
  TCCR0 = (1<<CS01); // Prescaler 8

  // Overflow Interrupt erlauben
  TIMSK |= (1<<TOIE0);

  // Global Interrupts aktivieren
  sei();

  while(1)
  {
    /* Sonstige Aktionen */
  }
}

/*
Der Overflow Interrupt Handler
wird aufgerufen, wenn TCNT0 von
255 auf 0 wechselt (256 Schritte),
d.h. ca. alle 2 ms
*/
#ifndef TIMER0_OVF_vect
// Für ältere WinAVR Versionen z.B. WinAVR-20071221
#define TIMER0_OVF_vect TIMER0_OVF0_vect
#endif

ISR (TIMER0_OVF_vect)
{
// Led blinken lassen ...
}

Kann mir dabei jemand weiterhelfen? Wäre super!
MfG Cuadrado

von Daniel V. (danvet)


Lesenswert?

Cuadrado schrieb:
> Ist so etwas möglich? Ich habe in der Anleitung folgenden Quellcode
> gefunden, hierbei blinkt zwar immer meine LED, aber das Hauptprogramm
> lässt sich nicht mehr ausführen.

Hast du das schon ausprobiert, oder ist das eine Vermutung?

von asterix (Gast)


Lesenswert?

Daniel V. schrieb:
> Cuadrado schrieb:
>> Ist so etwas möglich? Ich habe in der Anleitung folgenden Quellcode
>> gefunden, hierbei blinkt zwar immer meine LED, aber das Hauptprogramm
>> lässt sich nicht mehr ausführen.
>
> Hast du das schon ausprobiert, oder ist das eine Vermutung?

Ja, wie wurde das getestet? Dein Hauptprogramm ist leer und somit 
passiert dort auch nicht viel

von Cuadrado (Gast)


Lesenswert?

Also, während in der ISR eine LED immer blinkt, soll im Hauptprogramm 
einfach mal eine andere LED etwas schneller blinken. Dieses Programm 
habe ich ausprobiert. Die ISR LED hat funktioniert, an PD1 lagen aber 
immer +5V an.

#define F_CPU 1000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

int main(void)
{

  DDRD = 0xFF;

  // Timer 0 konfigurieren
  TCCR0 = (1<<CS01); // Prescaler 8
  // Overflow Interrupt erlauben
  TIMSK |= (1<<TOIE0);
  // Global Interrupts aktivieren
  sei();

  while(1)
  {

  PORTD |= (1<<PD1);
  _delay_ms(100);
  PORTD &= ~(1<<PD1);
  _delay_ms(100);

  }
}


ISR (TIMER0_OVF_vect)
{
PORTD |= (1<<PD0);
_delay_ms(500);
PORTD &= ~(1<<PD0);
_delay_ms(500);

}

von Daniel V. (danvet)


Lesenswert?

Cuadrado schrieb:
> ISR (TIMER0_OVF_vect)
> {
> PORTD |= (1<<PD0);
> _delay_ms(500);
> PORTD &= ~(1<<PD0);
> _delay_ms(500);
>
> }

Scherzkeks! Niemals in ISR ein delay() verwenden!
Überleg mal warum.

von npn (Gast)


Lesenswert?

Kann ich mir gut vorstellen, der ist ja in der ISR ausschließlich mit 
Warten beschäftigt und kommt gar nicht zum Hauptprogramm. Und nach dem 
Verlassen der ISR macht er einen Takt im Hauptprogramm und springt dann 
sofort wieder in die ISR. Ich vermute, wenn du lange genug wartest, wird 
sich auch im Hauptprogramm was tun. Das dauert aber lange (1 Takt pro 
Sekunde).

Lösung: Delay raus aus der ISR und das ganze mit Timern machen.

von Cuadrado (Gast)


Lesenswert?

Weil die An/Aus-Zeit, in diesem Fall 1s länger ist als die Zeit bis die 
nächste ISR ausgelöst wird?

von Daniel V. (danvet)


Lesenswert?

Cuadrado schrieb:
> Weil die An/Aus-Zeit, in diesem Fall 1s länger ist als die Zeit bis die
> nächste ISR ausgelöst wird?

Hmm...
Eine ISR ist ASAP wieder zu verlassen, da sie das Hauptptogramm 
unterbricht.

Wie in der Anleitung vermerkt wurde, wird TIMER0_OVF alle 2ms 
aufgerufen.
Wenn du da ein delay von 500ms machst, dann kannst du den Timer auch 
gleich weglassen (du häbgst die ganze Zeit im Interrupt fest). Da gehört 
ein Zähler rein, der auf 500ms abgefragt wird, dann toggelst du den Pin 
und setzt den Zähler zurück.

Ach ja, wenn du schon ein Tutorial liest, solltest du auch alles lesen.
So könnte man es machen:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR#Timer2_im_Asynchron_Mode

: Bearbeitet durch User
von Cuadrado (Gast)


Lesenswert?

Ok, danke für die Tipps!

Also so funktioniert schonmal was, allerdings muss die LED in der ISR 
sehr schnell blinken und die andere auch. Naja, da muss ich mich wohl 
noch weiter einlesen, aber das Prinzip ist jetzt klarer geworden. :D

#define F_CPU 1000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

int main(void)
{

  DDRD = 0xFF;

  // Timer 0 konfigurieren
  TCCR0 = (1<<CS02); // Prescaler 256
  // Overflow Interrupt erlauben
  TIMSK |= (1<<TOIE0);
  // Global Interrupts aktivieren
  sei();

  while(1)
  {

  PORTD |= (1<<PD1);
  _delay_ms(200);
  PORTD &= ~(1<<PD1);
  _delay_ms(200);

  }
}


ISR (TIMER0_OVF_vect)
{

int zeit;

  for (zeit=0; zeit<1000; zeit++)
  {
    PORTD |= (1<<PD0);
  }

  PORTD &= ~(1<<PD0);

}

von npn (Gast)


Lesenswert?

Cuadrado schrieb:
> ISR (TIMER0_OVF_vect)
> {
>
> int zeit;
>
>   for (zeit=0; zeit<1000; zeit++)
>   {
>     PORTD |= (1<<PD0);
>   }
>
>   PORTD &= ~(1<<PD0);
>
> }

Du wartest doch schon wieder in der ISR!
Mach die Zeit mit dem Timer. Und in der ISR wird dann nur noch der Port 
gesetzt und dann gehts wieder raus.

von Bastler (Gast)


Lesenswert?

>ISR (TIMER0_OVF_vect)
>{
>
>int zeit;
>
>  for (zeit=0; zeit<1000; zeit++)
>  {
>    PORTD |= (1<<PD0);
>  }
>
>  PORTD &= ~(1<<PD0);
>
>}

Das ist doch der gleiche Käse wie mit dem delay!


ISR (TIMER0_OVF_vect)
{

zeit++;

  if (zeit >= 1000)
  {
    PORTD |= (1<<PD0);
    zeit = 0;
  }
  else
  {
  PORTD &= ~(1<<PD0);
  }

Sowas in der Art müsste gehen, da wird in der ISR nur eine Abfrage 
gemacht und keine Zeit verplempert.
}

von tsag (Gast)


Lesenswert?

Cuadrado schrieb:
>
>
> ISR (TIMER0_OVF_vect)
> {
> PORTD |= (1<<PD0);
> _delay_ms(500);
> PORTD &= ~(1<<PD0);
> _delay_ms(500);
>
> }

AUTSCH!!!


das warten übernimmt doch schon der timer für dich stelle ihn so ein, 
dass die isr alle 500ms aufgerufen wuird und toggle den pin... gut is

von Felix P. (fixxl)


Lesenswert?

Du kannst dir ausrechnen, wie lange ein Zählschritt bei einer 
Taktfrequenz von 1 MHz und einem Prescaler von 256 dauert. Da der Zähler 
ein 8-Bit-Zähler ist, dauert es 256 Zählschritte, bis der Timer 
überläuft (ca. 65 ms).

Wenn das kürzer ist als die Zeit, die zwischen den Umschaltvorgängen 
vergehen soll, musst du in der Interruptroutine eine Variable hochzählen 
und per "if" prüfen, ob der gewünschte Wert schon erreicht ist. Wenn ja, 
kannst du den Pinzustand entweder mittels XOR oder durch Beschreiben des 
PIN-Registers umschalten.
1
int main (void) {
2
volatile uint8_t zeit = 0;
3
...
4
}
5
6
7
ISR (TIMER0_OVF_vect) {
8
  zeit++;
9
10
  if (zeit > 8) {
11
    PORTD ^= (1<<PD0);
12
    zeit = 0;
13
  }
14
}

: Bearbeitet durch User
von Hans (Gast)


Lesenswert?

Die Variable "zeit" muss außerhalb von main stehen. Das volatile ist in 
diesem Fall nicht nötig, da sie ja nur in der ISR verwendet wird.

von oldmax (Gast)


Lesenswert?

Hi
Wie immer sitzt ein Fehler vor dem µC. Merke: Ein Controller macht genau 
das, was du ihm sagst und nicht das, was du glaubst, ihm gesagt zu 
haben. Das ist der Unterschied zwischen Mensch und Maschine. Ich kann 
leider kein C und deshalb auch nicht mit Code weiterhelfen, aber merk 
dir eines: Eine ISR soll etwas ganz schnell zwischendurch erledigen, das 
keinen Aufschub erlaubt. Wenn ein Postbote an der Tür klingelt und du 
der Meinung bist, das es ja Zeit hat darauf zu reagieren, ist er wieder 
weg. Also, alles liegen lassen, die Post holen und dann weitermachen. 
Lesen kannst du dann in Ruhe. Ich glaub, die wenigsten Menschen lesen im 
Beisein des Postboten erst mal die Post, bevor sie wieder zurück in die 
Wohnung gehen. Es könnt sonst sein, das des Essen verkokelt oder das 
Bügeleisen ein neues Emblem auf das Hemd gezaubert hat.
MAch folgendes:
In deiner ISR, die alle 2mSek. aufgerufen wird, zählst du erst mal bis 
50. Das entspricht der Zeit von 0.1 Sek. In einem beliebigen Byte setzt 
du dann ein Bit. dann zählst du bei einem Überlauf bei 50 noch mal bis 
10. Das entspricht der Sekunde.
Etwas so:
ISR:
  INC Zähler1
  Zähler1<50 dann ende_ISR
  Set Bit_100mSek
  INC  Zähler2
  Zähler2<10 dann ende_ISR
  Set Bit_Sek
ende_ISR

Das ist eine überschaubare ISR. Im Hauptprogramm fragst du diese Bits 
ab:

 If Bit_100mSek dann
     .....
     Reset Bit100mSek
 endIf
 If Bit_Sek dann
     ...
     Reset Bit_Sek
 EndIF

Sorry, besser kann ich es nicht beschreiben, aber diesen Pseudocode 
sollte jeder in seine Sprache übersetzen können.
Gruß oldmax

von Paulchen_P (Gast)


Lesenswert?

Bastler schrieb:
>
>
> ISR (TIMER0_OVF_vect)
> {
>
> zeit++;
>
>   if (zeit >= 1000)
>   {
>     PORTD |= (1<<PD0);
>     zeit = 0;
>   }
>   else
>   {
>   PORTD &= ~(1<<PD0);
>   }
>
> }

um mit Deinen Worten zu antworten:

Das ist doch der gleiche Käse wie mit dem delay!

 Da kann man die ISR auch gleich weglassen.

> Sowas in der Art müsste gehen,
 nein, die blitzt höchstens ab und zu mal.

von Dieter F. (Gast)


Lesenswert?

Cuadrado schrieb:
1
 ISR (TIMER0_OVF_vect)     // Led blinken lassen ... 
2
 {
3
    PORTD ^=  (1<<PD0);    // LED togeln
4
 }

Ich würde es mal mit "toggeln" (s.o.) versuchen; d.h. bei jedem Aufruf 
der ISR wird der aktuelle Zustand der LED invertiert.
Über den Timer stellst Du ein, in welchem Rhythmus das passiert und ob 
es passiert.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Cuadrado schrieb:
> Also, während in der ISR eine LED immer blinkt, soll im Hauptprogramm
> einfach mal eine andere LED etwas schneller blinken. Dieses Programm
> habe ich ausprobiert. Die ISR LED hat funktioniert, an PD1 lagen aber
> immer +5V an.
Warum muss denn eine LED in der ISR blinken? Meine wichtigste ISR in 
jedem Programm stellt nur einen Zähler zur Verfügung, der pro ms um 1 
hochgezählt wird. Und der Ganze Rest läuft in der Hauptschleife. Hier 
mal 3 LEDs, die "gleichzeitig" mit unterschiedlicher Frequenz blinken:
1
  volatile unsigned long tiAkt=0;
2
  unsigned long tiLED1=0;
3
  unsigned long tiLED2=0;
4
  unsigned long tiLED3=0;
5
6
  ISR --> zählt jede ms die tiAkt um 1 hoch
7
8
  main () {
9
 
10
   while(1) {
11
    if (tiAkt>tiLED1) {  // Zeit für LED1 abgelaufen
12
      tiLED1+=300;       // nächsten Zeitpunkt ausrechnen
13
      LED1=~LED1;        // Led toggeln
14
    }
15
    if (tiAkt>tiLED2) {  // Zeit für LED2 abgelaufen
16
      tiLED2+=500;       // nächsten Zeitpunkt ausrechnen
17
      LED2=~LED2;        // Led toggeln
18
    }
19
    if (tiAkt>tiLED3) {  // Zeit für LED3 abgelaufen
20
      tiLED3+=700;       // nächsten Zeitpunkt ausrechnen
21
      LED3=~LED3;        // Led toggeln
22
    }
23
24
    // Und hier das tun, was sonst noch zu tun ist.
25
    // Aber  nichts, womit unnötig Zeit verplempert wird!
26
    // Also kein delay() oder nischtnutzige Zählschleifen.
27
    // Als Faustregel: die Hauptschleife muss in maximal 10ms 
28
    // einmal komplett durchlaufen werden.
29
  }
30
 }

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