Forum: Compiler & IDEs mal wieder ICP! Werte immer identisch.


von Axel L. (lemmi4711)


Lesenswert?

Hallo,

ich werde noch irre...

Ich will Impulse von 10-80ms (in 10ms Schritten) im 100ms Abstand 
messen.
Für die Signalerzeugung wird auch ein Mega8 verwendet. Laut Oszi haben 
die Zeiten eine Toleranz von ~0,2ms.

Ich dachte, zum Erfassen der Zeiten ist der ICP-Pin ideal, da man die 
Flankenerkennung umschalten kann. Aber ich kann machen was ich will, die 
Timerwerte sind immer identisch und das Ergebnis ist dann logischerweise 
immer 0;
Es soll erstmal die Zeitdifferenz zwischen steigender und fallender 
Flanke ausgegeben werden.

Text "Impulsdauer:" wird angezeigt...
Pulsdauer wird auch angezeigt, aber immer 0...
Flanke1 und Flanke2 aus der ISR werden auf dem LCD mit verschiedenen 
Werten (beide Variablen immer gleich!) angezeigt ...

Die LCD-Routine ist nur testweise drin (einfacher...) und funktioniert 
einwandfrei! In der ISR sind die LCD-Aufrufe nur drin, damit ich sehe, 
welche Werte ausgegeben werden. Ob die nun da drin sind oder nicht ist 
egal, der Effekt ist der selbe.
Später soll dann eine 7-Segment Anzeige verwendet werden, aber bevor ich 
mich da weiter mache, sollte erstmal die Berechnung funktionieren...


Ich hoffe Ihr könnt mir weiterhelfen, hier ist der Code:


#define F_CPU 8000000UL

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

volatile uint16_t Flanke1;
volatile uint16_t Flanke2;
uint16_t Pulsdauer;
int a;



ISR (TIMER1_CAPT_vect)
{
  if (TCCR1B & (1<<ICES1))           // Test auf steigende Flanke
  {
    Flanke1 = ICR1;                  // ICP Register = Flanke 1
    TCCR1B &=~(1<<ICES1);            // ICP1 auf fallende Flanke
    lcd_xy(0,2);
    lcd_Var(Flanke1);

  }

  if (!(TCCR1B & (1<<ICES1)))        // Test auf fallende Flanke
  {
    Flanke2 = ICR1;                  // ICP Register = Flanke 2
    TCCR1B |= (1<<ICES1);            // ICP1 auf steigende Flanke
    lcd_xy(9,2);
    lcd_Var(Flanke2);

  }
}


int main (void)
{
  PORTB = 0b00111110;
  DDRB  = 0b00111110;                // Port B0 als Eingang (ICP)

  TIMSK  |= (1<<TICIE1);             // ICP enable
  TCCR1B |= (1<<CS10)|(1<<CS12)|(1<<ICES1);
  // Timer1 => 1/1024 CPU-Clock, ICP steigende Flanke

  sei();                             // Interruptfreigabe

  while (1)
  {
    Pulsdauer = (Flanke2 - Flanke1); // Pulsdauer ausrechnen
    cli();
    // für LCD Ausgabe die Interrupts sperren, führt sonst zu Störungen
    // in den LCD-delays...
    lcd_xy(0,0);
    lcd_String("Impulsdauer:");
    lcd_xy(0,1);
    lcd_Var(Pulsdauer);
    _delay_ms(100);
    sei();                           // Interrupts wieder freigeben
  }
  return (0);
}

von Oliver (Gast)


Lesenswert?

Überleg mal, warum dein Programm immer beide if-Blöcke in der ISR 
durchläuft. Ein "else" an Stelle des zweiten if's dürfte das Problem 
lösen.

Oliver

von Stefan E. (sternst)


Lesenswert?

Außerdem:

1. cli und sei sind falsch positioniert. Du klammerst damit genau den 
falschen Bereich.

2. Welcher Controller?
Z.B. Zitat Datenblatt ATmega8:
1
After a change of the edge, the Input Capture Flag (ICF1) must be
2
cleared by software (writing a logical one to the I/O bit location).

3. Die LCD-Ausgaben im Interrupt sind auch eher suboptimal.

4. Solltest du mit dem Berechnen von Pulsdauer nicht so lange warten, 
bis in Flanke1/2 auch was sinnvolles drin steht?

von Axel L. (lemmi4711)


Lesenswert?

Hallo, Leute...

Mit Eurer Hilfe habe ich es nun geschafft.

Es sollte ein Zuordnungsgerät für 8 Leitungen werden, bestehend aus 
Sender und Empfänger...

@ Oliver: Hauptproblem war die 2. if Abfrage in der ISR. Ich musste dann 
nur noch eine Variablenzuweisung hinzufügen, welche ich in 'main' 
abfrage. Dann hat alles funktioniert.

@ Stefan E.: Nun gut, es musste natürlich gewartet werden, bis beide 
Messungen abgeschlosen sind. Aber dennoch ist Dein Zitat aus dem ATMEGA8 
Datenblatt falsch!!!

Sobald der Interrupt in der ISR aufgerufen wird, wird ICF1 automatisch 
gelöscht...

Das richtige Zitat lautet:

ICF1 is automatically cleared when the Input Capture Interrupt Vector is 
executed. Alternatively, ICF1 can be cleared by writing a logic one to 
its bit location.

cli() & sei() habe ich korrigiert...

Über die anderen Kommentare werde ich nicht eingehen, da Du 
offensichtlich meine Info dazu nicht gelesen hast.

Da ich das Projekt interressant finde, werde ich das Projekt in der 
Codesammlung veröffentlichen...


Danke und Gruß an Euch....

von Stefan E. (sternst)


Lesenswert?

Axel Lemke schrieb:

> @ Stefan E.: Nun gut, es musste natürlich gewartet werden, bis beide
> Messungen abgeschlosen sind. Aber dennoch ist Dein Zitat aus dem ATMEGA8
> Datenblatt falsch!!!

Das Zitat steht genau so wie von mir gepostet im Datenblatt (per 
Copy&Paste rausgeholt). Glaubst du ernsthaft, ich hätte mir den Text 
einfach ausgedacht?


> Sobald der Interrupt in der ISR aufgerufen wird, wird ICF1 automatisch
> gelöscht...
>
> Das richtige Zitat lautet:
>
> ICF1 is automatically cleared when the Input Capture Interrupt Vector is
> executed. Alternatively, ICF1 can be cleared by writing a logic one to
> its bit location.

Lies mein Zitat nochmal genau. Es bezieht sich ja gar nicht auf das 
Flaglöschen nach einem Interrupt, sondern darauf, dass man nach dem 
Ändern der Flankenerkennung (also Bit ICES1) das Flag löschen muss. Das 
bedeutet implizit nichts anderes, als dass das Ändern des Bit ICES1 als 
Nebeneffekt ICF1 setzt (oder zumindest unter bestimmten Umständen setzen 
kann).

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.