Forum: Compiler & IDEs Interrupt Probleme


von harry (Gast)


Lesenswert?

Hallo, folgendes Programm sollte eigentlich eine LED mit einem
Sekundentakt ein bzw. auschalten. Der Timer wurde so gesetzt, dass er
mit einem 1024 des Systemtakt aufwärtszählt. Leider bleibt die LED
immer an. An was kann das liegen ? Ich habe auch schon die
Vergleichswerte in der Interruptroutine erhöht - führt jedoch auch zu
keinem sichtbaren Unterschied.

Bin für jede Hilfe dankbar.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr\signal.h>

volatile unsigned short ledon = 1;

void main(void)
{

outp (0x02,DDRB);

outp ((1<<CS02) | (0<<CS01) | (1<<CS00), TCCR0);

sbi(TIMSK,TOIE0);/* enable TIMER0 OVI */

  //timer_enable_int(1 << TOIE0);

sei ();

while (1) {}

}



SIGNAL(SIG_OVERFLOW0) {
    if (ledon < 4000) {
      ledon ++;
      cbi(PORTB,1); // -> LED ON
    }
    else
    {
      ledon ++;
      sbi(PORTB,1); // -> LED OFF
      if (ledon > 8000)
        ledon = 0;
    }

}



::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Beobachtungen:

-> Interrupt wird genau einmal aufgerufen, jedoch anschliessend nicht
mehr ( meiner Meinung nach )

-> die Interruptroutine werden häufiger als jede 1/4000 sekunde
aufgerufen ( jedoch auch bei Anpassung der Vergleichswerte keine
sichtbare Veränderung des Programms )

von Joerg Wunsch (Gast)


Lesenswert?

> Interrupt wird genau einmal aufgerufen, jedoch anschliessend nicht
> mehr ( meiner Meinung nach )

Hast Du auch wirklich lange genug gewartet? ;-)

Du schreibst nicht, welchen Takt Du benutzt, gehen wir mal von 4 MHz
aus.

$ echo 'scale=5; (4000*256*1024)/4000000' | bc
262.14400

Das wäre die halbe Periode Deiner LED, als die Einschaltdauer, mehr
als 4 Minuten...

Generelle Bemerkungen:

> #include <avr\signal.h>

Bitte Vorwärtsschrägstriche benutzen, sonst geht's nur unter
DOS^H^H^HWindows.

> volatile unsigned short ledon = 1;

Das `volatile' wäre hier gar nicht notwendig, da die Variable ja nur
innerhalb der ISR benutzt wird.

> void main(void)

Das widerspricht dem C-Standard und wäre nur mit der Compiler-Option
-ffreestanding erlaubt.  Die wiederum verhindert andere Optimierungen
auf Bibliotheksebene, die manchmal sinnvoll sein können.

int main(void)

ist besser.

> outp (0x02,DDRB);

inp() und outp() bitte nicht mehr benutzen.

>     if (ledon < 4000) {
>       ledon ++;

Ziemlich umständlich geschrieben meiner Meinung nach.

Hier meine Version (ungetestet).  Zeit ist nicht exakt eine Sekunde,
aber das hängt am Ende von Deinem CPU-Takt ab.

#include <inttypes.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>

#define LEDPIN PB2
#define CPUCLK 4000000UL

static void
ioinit(void)
{
  DDRB = _BV(LEDPIN);
  /*
   * LED am Anfang einschalten, falls Ausgang H-aktiv ist:
   */
  /* PORTB = _BV(LEDPIN); */

  TCCR0 = _BV(CS02) | _BV(CS00);  /* CPUCLK / 1024 */
  TIMSK = _BV(TOIE0);
  sei();
}

SIGNAL(SIG_OVERFLOW0)
{
  static uint8_t cnt;

  if (++cnt == (uint8_t)(CPUCLK / (1024UL * 256))) {
    cnt = 0;
    PORTB ^= _BV(LEDPIN);
  }
}

int
main(void)
{

  ioinit();

  for (;;)
    ;

  /* NOTREACHED */
  return 0;
}

Mögliche Verfeinerung:

In dieser einfachen Applikation kann es sich lohnen, statt einer
Speicherstelle ein Register für cnt zu benutzen.  Die Deklaration
von cnt innerhalb der Interruptroutine ersetzen durch

register uint8_t cnt asm("r2") = 0;

auf globalem Niveau.

von harry (Gast)


Lesenswert?

Hallo Jörg,

dein Programm funktioniert prächtig. Lediglich PB2 muss zu PB1 geändert
werden. Was die Wartezeit betrifft hattest du Recht - ich habe die
Werte in meinem Programm angepasst und siehe da es funktioniert doch
:). Ich habe vergessen dass der Timer auf 256 zählt. Ziemlich
bescheuert eigentlich, aber ich habe im Moment noch keine serielle
Schnittstelle, deshalb fällt das Debuggen ziemlich schwer.

Ich habe mir beinahe gedacht, dass eine Bemerkung zum Aufbau des
Quellcodes kommt. Gewöhnlicherweise programmiere ich nicht so
umständlich.

Wieso steht jeweils _bv vor der Benutzung einiger Konstanten ?


vielen Dank für deine Hilfe !!!

mfG Harald

von Joerg Wunsch (Gast)


Lesenswert?

> Lediglich PB2 muss zu PB1 geändert werden.

Ja sorry, das hatte ich vermasselt, nicht richtig geguckt.

> Wieso steht jeweils _bv vor der Benutzung einiger Konstanten

(Es ist übrigens _BV() -- C unterscheidet zwischen Groß- und
Kleinschreibung!)

Wofür der Name BV steht, weiß keiner mehr so richtig. ;-)  ``bit
value'' oder sowas.  Es setzt die als Paramter übergebenen Bitnummer
in einen Integer-Wert um, den man einem Register zuweisen kann, als
Bitmaske benutzen o. ä.  Das ist nichts anderes als das Herumschieben
eines 1-bits:

#define _BV(x) (1 << (x))

es sieht nur (meiner Meinung nach) ein bißchen übersichtlicher aus im
Quellcode.  Wenn Du portabel zu anderen C-Compilern bleiben willst,
darfst Du es aber nicht benutzen, da es diesen Makro dort nicht gibt.

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.