www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Probleme mit ICP/OVF Interrupts zur Frequenzmessung


Autor: Marco W. (watz)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Mahlzeit zusammen !!

Ich hab mir ein kleines Stück Code geschrieben um am Mega8 per ICP1 
Frequenzen zu messen. Prinzipiell funktioniert das alles recht gut. Zum 
Testen hab ich bisher eine über Timer2 im CTC Mode erzeuge Frequenz von 
genau 10000 Hz am OC2 pin gemessen. Getaktet ist der Mega8 mit einem 
12Mhz Quarz.
Im Anhang ist der Code dazu. Wichtig ist mir, dass nur für die Dauer 
einer Messung die Input Capture und Overflow Interrupts laufen, da ich 
später noch andere zeitkritische Dinge per Interrupts erledigen möchte.

Der Code der main() Loop sieht etwa so aus:
  FreqCountInitialize();

  uint8_t nSecondsPassed = 0;
  uint8_t nFreqCount = 0;
  for(;;)
  {
    // delay 100ms
    for(uint8_t i = 0; i < 10; i++)
    {
      _delay_ms(10);
    }

    if(nFreqCount == 0)
    {
      nFreqCount = 1;
      nSecondsPassed = 0;
      FreqCountStart();
    }
    else
    {
      nSecondsPassed++;
      if(FreqCountIsFinished())
      {
        double dResult = FreqCountGetResult();
        char szBuf[25] = {0};
        dtostrf(dResult, 14, 8, szBuf);
        DisplayWriteLine(szBuf);
        nFreqCount = 0;
      }
      else
      {
        if(nSecondsPassed > 3)
        {
          FreqCountStop();
          nFreqCount = 0;
          DisplayWriteLine("too low");
        }
        else
        {
          DisplayWriteLine("pending");
        }
      }
    }
  }

So erhalte ich etwa 5 Messungen die Sekunde.
Die 10000Hz werden bis auf die 8. Nachkommastelle genau berechnet.
Die ICP Tick Differenz zwischen zwei Peaks ist dann 1200, d.h. es 
sollten während der Messung eigentlich keine Overflows auftreten.
Da aber beide Timer den Takt letztendlich vom gleichen Quarz beziehen 
hatte ich das auch so erwartet.

Jetzt hab ich zwei "seltsame" Probleme die ich gern verstehen würde.

1.) Ich lasse FreqCountInitialize() weg und verschiebe den Code zum 
Initialisieren des Timers über TCCR1B vors Aktivieren der Interrupts in 
FreqCountStart(). Dadurch wird TCCR1B ab der zweiten Messung beschrieben 
obwohl der Timer schon entsprechend läuft. Das sieht dann etwa so aus:
void FreqCountStart()
{
  g_unState = STATE_FIRST;
  TCCR1B &= ~_BV(ICNC1);
  TCCR1B |= _BV(ICES1);
  TCCR1B &= ~(1 << 5);
  TCCR1B &= ~_BV(WGM13);
  TCCR1B &= ~_BV(WGM12);
  TCCR1B &= ~_BV(CS12);
  TCCR1B &= ~_BV(CS11);
  TCCR1B |= _BV(CS10);
  TIMSK |= _BV(TICIE1);
  TIMSK |= _BV(TOIE1);
}

So funktiert es dann auch, alle Messungen sind ok.
Ich änder den Code nochmal so ab, das TCCR1B komplett beschrieben wird 
und nicht jedes Bit einzeln:
void FreqCountInitialize()
{
  g_unState = STATE_FIRST;
  TCCR1B = _BV(ICES1) | _BV(CS10);
  TIMSK |= _BV(TICIE1);
  TIMSK |= _BV(TOIE1);
}

So ist jede Messung nach der ersten Schrott mit einem relativ konstanten 
Wert.
Das kann ich mir jetzt mal nicht erklären......wo liegt der Unterschied 
zum Bitweise beschreiben des TCCR1B ?
Das Problem verschwindet, wenn ich zwischen dem Initialisieren des 
Timers und dem Aktivieren der Interrupts ein "_delay_ms(1)" einfüge.
Ein "_delay_us(1)" langt nicht. Irgendwelche Ideen warum sich das so 
verhält ?

2.) Sporadisch, etwa jede 10. Messung, erhalte ich Unsinnige Werte weil 
der Overflow-Count nicht 0 ist. Oft steht er auf 1, weniger oft auf 19. 
Keine anderen werte. Die 19 Overflows konnte ich mir nur durch das 
Weiterlaufen des Overflow Interrupts nach der Messung erklären, denn die 
entsprechen ungefähr den 100ms bis zum Start der nächsten Messung. Das 
hab ich scheinbar in den Griff bekommen indem ich den Code des Overflow 
Interrupt Handlers so umgebaut habe:
ISR(TIMER1_OVF_vect)
{
  if(g_unState != STATE_IDLE)
  {
    g_unOverflows++;
  }
}

Da ich allerdings beim Abschluss der Messung den Interrupt deaktiviere 
und sogar das Overflow Flag lösche, wie kann das sein ?
Mach ich beim Deaktivieren des Overflow Interrupts im ICP Interrupt was 
falsch ?
Ich vermute, ein Fehler hier auch die Ursache für den sproadischen 
Overflow-Count 1 ist.

Es geht mir hier nur darum zu verstehen warums der Code nicht 100%ig 
tut.
In meinem Anwendungsfall kann ich gut damit leben, aber ich bin halt 
neugierig.
Vielleicht nützen ein paar Augenpaare mehr ja was....

Danke & Gruß,
Watz

Autor: Marco W. (watz)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Falls es noch jemanden interessiert....hab zumindest 2.) mittlerweile 
selbst in den Griff bekommen. Als entscheidend hat sich das Zurücksetzen 
des Interrupt-Flags TICIE1 vor dem Aktivieren der ISR herausgestellt. Da 
der Timer mit Input Capture weiterlief wird die ISR sofort aufgerufen, 
allerdings hat dann das ICP Register noch den Timer Tick eines "alten" 
Events.

Gruß,
Watz

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.