www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik 5 Zeiten mit ICP messen -> Problem mit den Timerwerten (ATmega8 und C)


Autor: Joe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich bin gerade unterwegs und habe meinen Codeschnippsel von der ISR des 
InputCaptures nicht parat.
Ich versuche trotzdem mein Problem zu schildern, kann nämlich erst heute 
Abend den Code posten:

Ich will mit dem ICP-Pin des ATmega8 Zeiten messen (positive Flanken).
Für einen kompletten Zyklus muss ich 5 Zeiten einlesen, um damit 
weiterarbeiten zu können.

Mein Problem ist, dass mein Timer NIE beim ersten Interrupt bei 0 
anfängt zu zählen.
So habe ich festgestellt, dass der erste Zeitwert auch mal -253 betragen 
kann, ich möchte aber mit positiven Zahlen rechnen, darum soll der Timer 
bei 0 beginnen, wenn der erste Interrupt auftritt.
Wenn die 5 Messungen komplett sind, wird diese ISR gesperrt und die 
Werte werden verarbeitet.
Sobald das erledigt ist, wird die ISR wieder freigegeben und der 
Timerwert mit ICR1 = 0 auf 0 gesetzt, aber mir scheint es, dass der 
Timer dann wieder von alleine anfängt zu laufen. Der soll aber erst bei 
einer positiven Flanke wieder anfangen zu laufen, wie kann ich das dem 
ATmega8 in C beibringen?

MfG, Joe

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joe schrieb:

> So habe ich festgestellt, dass der erste Zeitwert auch mal -253 betragen
> kann,

Timerwerte sind grundsätzlich unsigned!

> ich möchte aber mit positiven Zahlen rechnen, darum soll der Timer
> bei 0 beginnen, wenn der erste Interrupt auftritt.

Ist unnötig.
Die Differenz zweier Timerwerte ist (wegen unsigned) immer richtig, 
solange nicht mehr als 1 Timer Overflow und die maximale Anzahl an 
Timerticks (65536) dazwischen liegt.


> Sobald das erledigt ist, wird die ISR wieder freigegeben und der
> Timerwert mit ICR1 = 0 auf 0 gesetzt, aber mir scheint es, dass der
> Timer dann wieder von alleine anfängt zu laufen.

Nur der Vollständigkeit halber:
Ein Timer läuft, sobald er einen Vorteiler zugewiesen hat.
Soll ein Timer stehen bleiben, muss man den Vorteiler abschalten.

Aber wie gesagt: Ist unnötig, wenn du garantieren kannst, dass zwischen 
2 ISP Interrupts nicht mehr als 1 Overflow liegt. Einfach unsigned 
rechnen ist ausreichend.

Autor: Joe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>> So habe ich festgestellt, dass der erste Zeitwert auch mal -253 betragen
>> kann,
> Timerwerte sind grundsätzlich unsigned!

Ich habe es so definiert (vereinfacht, da ich den Code nicht bei mir 
habe):

volatile unsigned int Zeit;
Zeit = ICR1;
ausgabe(); --> da kommt dann auch mal ein -253 raus

> Aber wie gesagt: Ist unnötig, wenn du garantieren kannst, dass zwischen
> 2 ISP Interrupts nicht mehr als 1 Overflow liegt. Einfach unsigned
> rechnen ist ausreichend.
Ja, das kann ich garantieren (wenn mich mein Wissen nicht täuscht)!

Es handelt sich hierbei um eine Auswertung des UTI03, falls dir das 
etwas sagt?! Der gibt mir eine Art PWM aus, die ich auslesen und 
berechnen muss.
Wenn ich nun die 5 Werte eingelesen habe, müssten doch eigentlich 2 
Werte annähernd gleich sein, da der UTI mit den zwei kleinsten Werten 
den Startpunkt für die Messung angibt.
Lasse ich mir nun die 5 Messungen per UART ausgeben, so kann ich nicht 
feststellen, dass zwei Werte gleich sind.
Ich habe das ganze mal auf 8 Werte hochgesetzt, aber auch da gibt es 
keine zwei gleichen Werte.
Ich ziehe jedes Mal den alten Messwert vom neuen Messwert ab, um auf die 
Zeitdauer zu kommen.
Werte sehen aber so aus: 23654 # 20542 # -21 # -235 # -513 # 12 # 1358
Trennzeichen ist hier mal die Raute, Werte sind fiktiv^^
Da kann doch was nicht stimmen, oder?

Ist wohl besser, wenn ich den Code mal poste?!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joe schrieb:
>>> So habe ich festgestellt, dass der erste Zeitwert auch mal -253 betragen
>>> kann,
>> Timerwerte sind grundsätzlich unsigned!
>
> Ich habe es so definiert (vereinfacht, da ich den Code nicht bei mir
> habe):
>
> volatile unsigned int Zeit;
> Zeit = ICR1;
> ausgabe(); --> da kommt dann auch mal ein -253 raus

Bei einem unsigned Wert kann per Definition nichts negatives 
herauskommen. Wie der Name schon sagt hat ein unsigned Wert kein 
Vorzeichen.

> Ist wohl besser, wenn ich den Code mal poste?!

Das auf jeden Fall.

Autor: Joe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier auszugsweise der Code. Es fehlt nur die UART-Routine, die 
funktioniert aber und hab die wegen der besseren Übersicht weggelassen:

Bitte nicht meckern, der Code ist noch nicht sauber formatiert und 
dokumentiert...
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include <util/delay.h>

#ifndef F_CPU
#define F_CPU 3686400
#endif

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#define INTERRUPTein   TIMSK |= (1<<TICIE1) | (1<<TOIE1);  // Makro für Capture-Interrupt einschalten
#define INTERRUPTaus   TIMSK &= ~((1<<TICIE1) | (1<<TOIE1)); // Makro für Capture-Interrupt ausschalten

char *Stopp = "#";    // Stoppzeichen für den String
char string[20];    // Array 'string' mit 20 Zeichen festlegen

volatile unsigned int Messung[5];
volatile unsigned int Ergebnis;

volatile unsigned int Wcapture;
volatile unsigned int Wcapturealt;
volatile unsigned int Bloopcounter;

volatile unsigned char  NeueDaten;     // Job Flag

//initialisieren der I/O-Schnittstellen
void ioinit (void)
{
  DDRB = 0b00000110;            //PortB konfigurieren
  PORTB = 0b11000011;            
  DDRD = 0b00000010;            //PortD konfigurieren
  PORTD = 0b00010100;               
}

void ausgabe (void)
{
  itoa(Ergebnis, string, 10);  // 'Ergebnis' nach ASCII wandeln
  uart_puts(string);    // 'ASCII-Ergebnis' ausgeben
  uart_puts(Stopp);    // Stoppzeichen ausgeben

}

ISR( TIMER1_CAPT_vect )
{
Wcapture = ICR1;
Messung[Bloopcounter] = Wcapture - Wcapturealt;
Wcapturealt = Wcapture;
Bloopcounter++;

if(Bloopcounter == 6)
{
NeueDaten = TRUE;
INTERRUPTaus;
}
}

int main()
{
  TCCR1B = (1<<ICNC1) | (1<<ICES1)  | (1<<CS10); // Noise-Canceller, Input Capture Edge (pos. Flanke), kein PreScale
  TIMSK = (1<<TICIE1) | (1<<TOIE1); // Interrupts aktivieren, Capture + Overflow

  Wcapturealt = 0;
  Bloopcounter = 0;
  NeueDaten = FALSE;

  ioinit();  
  sei();

  while(1)
  {
  if(NeueDaten){

  unsigned char I;

  for (I=0; I<5; I++)
  {
  Ergebnis = Messung[I];
  ausgabe();
  }

ICR1 = 0;
Wcapturealt = 0;
Bloopcounter = 0;
TIFR  |= (1 << ICF1);  // Löschen des Interruptflags für ICR1
INTERRUPTein;
NeueDaten = FALSE;
}
_delay_ms(200);

  } // while(1)
} // main
Ich lasse mir die Timerwerte ausgeben, als Ergebnis bekomme ich:
-13688#-28739#28948#-23479#26793#

Das kann doch gar nicht sein! Wie kann der Timerwert negativ werden?

Autor: H.j.Seifert (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
kann sein, das itoa den Funktionswert als int nimmt. Bin mir aber nicht 
sicher. sprintf() funktioniert aber auf jeden Fall. Ja, unschön gross...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joe schrieb:

> void ausgabe (void)
> {
>   itoa(Ergebnis, string, 10);  // 'Ergebnis' nach ASCII wandeln

Da haben wir ja einen Übeltäter.

itoa übernimmt, wie der Name schon sagt, einen int. Ein int hat ein 
Vorzeichen.

utoa übernimmt, wie der Name schon sagt, einen unsigned.

> Messung[Bloopcounter] = Wcapture - Wcapturealt;

Passt doch.
Du musst nur darauf achten, dass Messung[0] keinen vernünftigen Wert 
hat.
Ist ja auch logisch. Dein erster Messwert kann erst dann vorliegen, wenn 
die ISR das zweite mal aufgerufen wird.

Autor: Joe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
itoa hat bei mir immer funktioniert, wenn ich ein INT auf die Reise 
durch die Kabel zum PC schicke! ;o) Daran kann es ja nicht liegen, denn 
selbst wenn ich die Werte berechne kommt Mist raus.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joe schrieb:
> itoa hat bei mir immer funktioniert, wenn ich ein INT auf die Reise
> durch die Kabel zum PC schicke!

Du hast aber keinen int.
Du hast einen unsigned int!

Und nur weil beide 2 Bytes haben, heisst das noch lange nicht, dass itoa 
für einen unsigned das passende Werkzeug ist.

itoa kann doch nicht feststellen, ob das was du ihm vorwirfst 
tatsächlich unsigned ist oder nicht. itoa übernimmt die beiden Bytes die 
du ihm vorwirfst und nimmt an, dass Bit 15 das Vorzeichenbit ist. Bei 
dir ist das aber kein Vorzeichen sondern Teil der Zahl!
Oder anders ausgedrückt: itoa arbeitet mit der Annahme, dass das was du 
ihm gibst ein Vorzeichen hat.

Autor: Joe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
utoa... und schon wieder was dazu gelernt! Kannte ich noch nicht! Super!
Da hab ich mit meinem itoa wohl immer Glück gehabt...

Ich ändere meinen Code mal ab und melde mich dann wieder.
Erstmal vielen Dank!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Es handelt sich hierbei um eine Auswertung des UTI03, falls dir das
> etwas sagt?! Der gibt mir eine Art PWM aus, die ich auslesen und
> berechnen muss.
> Wenn ich nun die 5 Werte eingelesen habe, müssten doch eigentlich 2
> Werte annähernd gleich sein, da der UTI mit den zwei kleinsten Werten
> den Startpunkt für die Messung angibt.

Es ist auch immer eine gute Idee, sich im Vorfeld auszurechnen in 
welcher Größenordnung die Messwerte in etwa liegen werden.

Autor: Joe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich glaub, ich steh aufm Schlauch...

Die ISR verlass ich jetzt bei Bloopcounter==6 (Bloopcounter fängt bei 0 
an).
Dann habe ich Messung[0] bis Messung[5]

Wenn ich nun Messung [1] bis Messung[5] addiere, kommen immer andere 
Timerwerte zum Vorschein. Ist das okay so?
Man, so langsam sehe ich den Wald vor lauter Bäumen wohl nicht mehr.

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>volatile unsigned int Messung[5];

Dein Array hat aber nur 5 Elemente (0-4)...

Autor: Joe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oh, hatte ich vergessen mitzuposten:
volatile unsigned int Messung[6];
So habe ich das jetzt.

Trotzdem verstehe ich nicht, warum sich die Timerwerte so dermaßen 
ändern, obwohl der UTI doch immer die selben "Perioden" bei gleicher 
Temperatur ausgeben sollte, oder?

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du capturest 4 Werte zwischen 5 steigenden Flanken, dann beginnst du von 
vorn. Aber die 5. Flanke war ja bereits die 1. Flanke des nächsten 
Zyklus. Mit jedem Messdurchgang verschiebt sich also dein Mess-Fenster 
um eine steigenden Flanke gegenüber dem Signal, und nur bei jedem 4. 
(oder 5.?) Durchgang deckt sich beides.

Übrigens:

> TIMSK |= (1<<TICIE1) | (1<<TOIE1);
Wo ist denn der Code zum Overflow-Interrupt?

> ICR1 = 0;
Diese Zeile erfüllt keine Funktion, schon gar nicht die oben von dir 
zugedachte Funktion "Timerwert auf 0 setzen".

Autor: Joe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Overflow-ISR ist noch drin:
ISR( TIMER1_OVF_vect )
{
  Overflows++;
}
Also läuft der Timer doch weiter, wenn ich den mit
> ICR1 = 0;
nicht zurücksetze?!
Dann muss ich die Overflows berücksichtigen, aber wie setze ich den 
Timer wieder auf 0? Das wär das beste!

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
1. "ICR1 = 0;" setzt den Timer nicht zurück. "TCNT1 = 0;" würde das 
machen. Aber lass es (siehe 3).

2. Du musst Overflows nur dann berücksichtigen, wenn die Zeit zwischen 
zwei steigenden Flanken größer sein kann wie ein kompletter 
Timerdurchlauf. Wenn das nicht passieren kann, musst du dir um die 
Overflows nicht die geringsten Gedanken machen. Die Subtraktion 
berücksichtigt einen Überlauf automatisch.

3. Dein eigentliches Problem ist der Fehler im logischen Ablauf, und die 
daraus resultierende nicht-Synchronität zwischen Signal und Messung. Für 
die Behebung dieses Problems brauchst du den durchlaufenden Timer.

Autor: Joe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zu 1: Danke, werd ich mir merken.

Zu 2: Ein Overflow brauche ich nicht zu berücksichtigen?!

Zu 3: Okay, also, der Timer läuft durch.
Ich führe nun 6 Capture-Messungen durch.
Die erste Messung kann ich verwerfen, und da der Zyklus aus 5 
Abschnitten besteht, sollte doch ein kompletter Zyklus in Messung[1] bis 
Messung[5] zu finden sein, richtig?
Nun muss ich die beiden kürzesten Signale herausfinden, damit ich weiß, 
wo der Zyklus startet. Oh, kann es sein, dass ich bei dieser Auswertung 
einen Fehler mache und veraltete Werte nehme, wenn z.B. die kürzesten 
Messungen bei Messung[3] und Messung[4] zu finden sind?
Ist jetzt blöd geschrieben, aber ich brauche, wenn ich diesen Zyklus 
nehme, Messung[1] und Messung[5] für die Berechnung.
Da der Timer aber fortlaufend ist, können im Grunde die berechneten 
Werte gar nicht stimmen, da diese gar nicht mehr aktuell sind.
Also muss ich am besten zwei Zyklen vom UTI einlesen um eine 
fortlaufende Messung zu bekommen...
Ist das also mein Problem?

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joe schrieb:

> Zu 2: Ein Overflow brauche ich nicht zu berücksichtigen?!

Nein. Wenn der alte Capture-Wert 65530 ist, und der nächste ist dann 100 
(also mit Überlauf dazwischen), dann ist das Ergebnis der Subtraktion 
neu-alt 106 (also korrekt).

> Zu 3: Okay, also, der Timer läuft durch.
> Ich führe nun 6 Capture-Messungen durch.
> Die erste Messung kann ich verwerfen, und da der Zyklus aus 5
> Abschnitten besteht, sollte doch ein kompletter Zyklus in Messung[1] bis
> Messung[5] zu finden sein, richtig?
> Nun muss ich die beiden kürzesten Signale herausfinden, damit ich weiß,
> wo der Zyklus startet. Oh, kann es sein, dass ich bei dieser Auswertung
> einen Fehler mache und veraltete Werte nehme, wenn z.B. die kürzesten
> Messungen bei Messung[3] und Messung[4] zu finden sind?

Woher soll ich das wissen? Ich kann mich schließlich nur auf das 
beziehen, was du bisher gepostet hast. Und in dem Code oben passiert 
nichts davon. Ich habe das hier:
> Wenn ich nun Messung [1] bis Messung[5] addiere, kommen immer andere
> Timerwerte zum Vorschein.
in Bezug auf den Code oben beantwortet.

> Da der Timer aber fortlaufend ist, können im Grunde die berechneten
> Werte gar nicht stimmen, da diese gar nicht mehr aktuell sind.

Sorry, aber das ist Blödsinn.
(oder ich verstehe nicht, was genau du damit meinst)

Autor: Joe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>> Wenn ich nun Messung [1] bis Messung[5] addiere, kommen immer andere
>> Timerwerte zum Vorschein.
Bezieht sich auf: Ich führe nun 6 Capture-Messungen durch.
Stimmt, der Code ist nun veraltet.

Also, jetzt noch mal auszugsweise neu:
#define INTERRUPTein   TIMSK |= (1<<TICIE1) | (1<<TOIE1);  // Makro für Capture-Interrupt einschalten
#define INTERRUPTaus   TIMSK &= ~((1<<TICIE1) | (1<<TOIE1)); // Makro für Capture-Interrupt ausschalten

volatile double W1;
volatile double W2;
volatile double W3;
volatile unsigned int Phase1;
volatile unsigned int Phase2;
volatile unsigned int Phase3;
volatile unsigned int Messung[10];

void ausgabe (void)
{
  utoa((int)Ergebnis, string, 10); // (int) ist ein "cast" - 'Ergebnis' nach ASCII wandeln
  uart_puts(string);    // 'ASCII-Ergebnis' ausgeben
}

ISR( TIMER1_CAPT_vect )
{
Wcapture = ICR1;
Messung[Bloopcounter] = Wcapture - Wcapturealt;
Wcapturealt = Wcapture;
Bloopcounter++;

if(Bloopcounter == 10)
{
NeueDaten = TRUE;
INTERRUPTaus;
}
}

int main()
{
  TCCR1B = (1<<ICNC1) | (1<<ICES1)  | (1<<CS10); // Noise-Canceller, Input Capture Edge (pos. Flanke), kein PreScale
  TIMSK = (1<<TICIE1) | (1<<TOIE1); // Interrupts aktivieren, Capture + Overflow

  Wcapturealt = 0;
  Bloopcounter = 0;
  NeueDaten = FALSE;

  ioinit();  
  uartinit();   

  sei();

  while(1)
  {
  if(NeueDaten){
  cli();

unsigned char X, I;

X=2;
I=3;

X = 2;

 for (I=3;I<6;I++)
 {
 if( Messung[X] > Messung[I] )
 {
 X = I;
 }
 }

 if(X==2)
 {
 Phase1 = Messung[2] + Messung[3];
 Phase2 = Messung[4];
 Phase3 = Messung[5];
 }
 if(X==3)
 {
 Phase1 = Messung[3] + Messung[4];
 Phase2 = Messung[5];
 Phase3 = Messung[6];
 }
  if(X==4)
 {
 Phase1 = Messung[4] + Messung[5];
 Phase2 = Messung[6];
 Phase3 = Messung[7];
 }
  if(X==5)
 {
 Phase1 = Messung[5] + Messung[6];
 Phase2 = Messung[7];
 Phase3 = Messung[8];
 }

W1 = Phase3 - Phase1;
W2 = Phase2 - Phase1;

W1 = W1*100000;
W3 = W1/W2;
W3 = W3/32;
W3 = W3*334;
W3 = W3/100;

Ergebnis = W3;
ausgabe();
Wcapturealt = 0;
Bloopcounter = 0;
TIFR  |= (1 << ICF1);  // Löschen des Interruptflags für ICR1
INTERRUPTein;
NeueDaten = FALSE;
sei();
}

Nun sollte ich 10 Elemente im Array haben, die ich verwerten kann.
Nur es passt noch nicht so ganz. Irgendwie will das Vergleichen auf die 
kürzesten Zeiten nicht klappen.
Ich bekomme zwar Werte ausgespuckt, aber die sind nicht wahr und 
schwanken echt extrem.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joe schrieb:

> Also, jetzt noch mal auszugsweise neu:

Auszugsweise ist immer schlecht.


> void ausgabe (void)
> {
>   utoa((int)Ergebnis, string, 10); // (int) ist ein "cast" - 'Ergebnis'
> nach ASCII wandeln

Was soll der Unsinn.
Entweder Ergebnis ist unsigned, dann ist utoa die richtige Funktion, 
oder es ist ein int, dann ist itoa die richtige Funktion.

Manchmal sind casts zwar notwendig, aber generell sind die meisten Casts 
einfach nur ein Zeichen, dass irgendetwas furchtbar schief geht.

    utoa( Ergebnis, string, 10 );


Und bitte arbeite an deinen Einrückungen.
Das ist absolut nicht lustig, wenn man ständig rauf und runter scrollen 
muss um rauszufinden, wo denn eine { wieder geschlossen wird und welcher 
Teil von welcher Bedingung abhängt.

> Ich bekomme zwar Werte ausgespuckt, aber die sind nicht wahr
> und schwanken echt extrem.

Fang damit an, die Werte vom UTI ohne irgendwelche Rechnereien 
auszugeben. Es ist sinnlos, die noch grossartig umzurechnen, wenn die 
Eingangswerte schon nicht stimmen.

Alte Weisheit: Garbage in, garbage out. Egal was du dazwischen machst, 
wenn die Eingangswerte nicht stimmen, können auch die Ergebnisee nicht 
stimmen.

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Und bitte arbeite an deinen Einrückungen.

... und auch bitte an deiner Programm-Kommentierung.
Das würde möglichen Unterstützern etwas meht helfen.

Autor: Joe (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
So, anbei nun mein kompletter Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include <util/delay.h>

#ifndef F_CPU
#define F_CPU 3686400
#endif

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#define BAUD 9600            // Baudrate
#define USARTSPEED (F_CPU/(BAUD*16L)-1)  // Formel zur Berechnung der Werte für UBBRH/UBBRL
#define LOW(x)  ((x) & 0xFF)      // Makro zum Lowbyte Zugriff eines Word
#define HIGH(x) (((x)  >> 8) & 0xFF)  // Makro zum Highbyte Zugriff eines Word
#define RS485RX  PORTD &= ~(1<<PD2)    // Makro für MAX487 als Empfänger
#define RS485TX  PORTD |= (1<<PD2)    // Makro für MAX487 als Sender
#define INTERRUPTein   TIMSK |=   (1<<TICIE1) | (1<<TOIE1);  // Makro für Capture-Interrupt einschalten
#define INTERRUPTaus   TIMSK &= ~((1<<TICIE1) | (1<<TOIE1)); // Makro für Capture-Interrupt ausschalten

char *Stop = "#";    // Stoppzeichen für den String
char string[20];    // Array 'string' mit 20 Zeichen festlegen

volatile uint8_t zeichen;        // zeichen als unsigned integer
volatile double Ergebnis = 0.0;      // Ergebnis als global deklarieren
volatile unsigned int Wcapture;      // Wcapture als unsigned int
volatile unsigned int Wcapturealt;    // Wcapturealt als unsigned int
volatile unsigned int Array[6];      // uINT-Array [0-5]
volatile unsigned char Bloopcounter;  // Impulszähler
volatile double W1;            // erster Wert zur Berechnung
volatile double W2;            // zweiter Wert zur Berechnung
volatile double W3;            // Ergebnis von der Berechnung
volatile unsigned int Phase1;      // Zeitwert für Formel
volatile unsigned int Phase2;      // Zeitwert für Formel
volatile unsigned int Phase3;      // Zeitwert für Formel
volatile unsigned char NeueDaten;    // Job-Flag
volatile unsigned char Overflows = 0;   // Anzahl der Timer Overflows die während
                                         // der Messung passiert sind

ISR(SIG_UART_RECV)           // UART Receive Interrupt wurde ausgelöst
{
  while ( !(UCSRA & (1<<RXC)) );  
  zeichen = UDR;          // den empfangenen Wert in 'zeichen' speichern
}

void UART_SendByte(uint8_t data)  // sendet ein Byte über das UART
{
  while (!(UCSRA & (1<<UDRE)));
  UCSRA|=(1<<TXC);          // vorangegangene Übertragung ist zu Ende
  UDR = data;            // 'data' ausgeben
}

void uart_puts(char *s)
{  
  RS485TX;                       // Sender an
  while (*s)
  {
    UART_SendByte(*s++);        // Daten senden 
  }
  while (!(UCSRA & (1<<TXC)));   // Warten, bis Senden komplett
  RS485RX;                       // Sender aus
  zeichen=0;
}


void uartinit (void)
{
  UBRRH = HIGH(USARTSPEED);            // Baudrate einstellen
  UBRRL = LOW(USARTSPEED);            // Baudrate einstellen
  UCSRB = (1<<TXEN) | (1<<RXEN) | (1<<RXCIE);  // Senden, Empfangen, ReceiveInt aktivieren
  UCSRC = (1<<URSEL)|(3<<UCSZ0);        // Frame Format setzen:8data, 1stop bit (URSEL=1 -> UCSRC->Settings werden genutzt)
}


void ioinit (void)
{
  DDRB  = 0b00000110;            //PortB konfigurieren
  PORTB = 0b11000011;            
  DDRD  = 0b00000010;            //PortD konfigurieren
  PORTD = 0b00010100;               
}

void ausgabe (void)
{
  utoa(Ergebnis, string, 10);  //'Ergebnis' nach ASCII wandeln
  uart_puts(string);      // 'ASCII-Ergebnis' ausgeben
  uart_puts(Stop);        // Stoppzeichen ausgeben
}

ISR(TIMER1_CAPT_vect)
{
  Wcapture = ICR1;                // ICR1-Zeit nach Wcapture speichern
  Array[Bloopcounter] = Wcapture - Wcapturealt;  // Zeitdifferenz berechnen und im Array speichern
  Wcapturealt = Wcapture;              // aktuelle ICR1-Zeit wird alte Zeit zur Berechnung
  Bloopcounter++;                  // Zähler erhöhen

    if(Bloopcounter == 6)    // wenn bis 6 gezählt...
    {
    NeueDaten = TRUE;      // ...Job-Flag setzen...
    Bloopcounter = 1;      // ...Zähler auf 1 setzen...
    INTERRUPTaus;        // und Interrupts vom Timer ausschalten
    }
}


ISR( TIMER1_OVF_vect )
{
  Overflows++;
}


int main()
{
  TCCR1B = (1<<ICNC1) | (1<<ICES1)  | (1<<CS10); // Noise-Canceller, Input Capture Edge (pos. Flanke), kein PreScale
  TIMSK = (1<<TICIE1) | (1<<TOIE1); // Interrupts aktivieren, Capture + Overflow

  Wcapturealt = 0;      // alte Capture-Zeit auf 0 setzen
  Bloopcounter = 1;      // Zähler auf 1 setzen
  NeueDaten = FALSE;      // Job-Flag auf 0 setzen

  ioinit();    // IOs initialisieren
  uartinit();    // UART initialisieren   
  sei();      // globale Interrupts zulassen



  while(1)
  {
  if(NeueDaten)
    {
    unsigned char X, I;

    X=2;

     for (I=3;I<6;I++)  // die zwei kürzesten Zeiten finden
       {
        if( (Array[X]) > (Array[I]) )
          {
          X = I;
          }
       }

     if(X==2)
     {
     Phase1 = Array[2] + Array[3];  // die zwei kürzesten Zeiten addieren
     Phase2 = Array[4];
     Phase3 = Array[5];
     }
   
     if(X==3)
     {
     Phase1 = Array[3] + Array[4];  // die zwei kürzesten Zeiten addieren
     Phase2 = Array[5];
     Phase3 = Array[2];
     }

     if(X==4)
     {
     Phase1 = Array[4] + Array[5];  // die zwei kürzesten Zeiten addieren
     Phase2 = Array[2];
     Phase3 = Array[3];
     }

     if(X==5)
     {
     Phase1 = Array[5] + Array[2];  // die zwei kürzesten Zeiten addieren
     Phase2 = Array[3];
     Phase3 = Array[4];
     }

    W1 = Phase3 - Phase1;    // Berechnungen durchführen [Start]
    W2 = Phase2 - Phase1;

    W1 = W1*100000;
    W3 = W1/W2;
    W3 = W3/32;
    W3 = W3*334;
    W3 = W3/100;        // Berechnungen durchführen [Ende] 

    Ergebnis = W3;        // Ergebnis der Berechnung ist W3 als double
    ausgabe();          // W3 wird per UART ausgegeben (als Ergebnis)
    Wcapturealt = 0;      // alte Capture-Zeit auf 0 setzen
    TIFR  |= (1 << ICF1);    // Löschen des Interruptflags für ICR1
    INTERRUPTein;        // Interrupts vom Timer einschalten
    NeueDaten = FALSE;      // Job-Flag auf 0 setzen
    
    } // if(NeueDaten)

  _delay_ms(200);        // 200ms warten

  } // while(1)

} // main

Hoffentlich habe ich das jetzt ein wenig übersichtlicher gemacht.
Die UART-Routine habe ich hier im Forum gefunden und muss sagen, dass 
die gut klappt. Habe mit meiner Drehzahlmessung keine Probleme bei der 
Ausgabe, verwende auch den RS485-Bus.

Zum Testen gibt mir dieses Programm nun kontinuierlich die errechneten 
Mess-Endergebnisse aus, die Werte sind aber nicht akzeptabel, siehe 
angehängtes Bild.
Es handelt sich jeweils um den Messwert vor der Raute.
Wenn ich die einzelnen Timerwerte ausgeben lasse, sieht das genau so aus 
wie auf dem Bild.
In einem früheren Post hatte ich schon mal darüber berichtet.
Zur Funktion des UTI habe ich eine PDF angehängt, ich verwende MODE5 
(ein PT100 in 4-Leiterschaltung).
Außerdem habe ich ein Basic-Code bekommen, den ich nach C umsetzen 
wollte.
Eventuell ist da auch schon der Fehler zu finden?!
Der Basic-Quellcode ist auch im Anhang.
Alle Anhänge sind in einer ZIP, geht hier wohl nicht anders?!

Ich weiß auf jeden Fall nicht mehr weiter, mir kommt es so vor, als ob 
die for-Schleife nicht hinhaut.
Achja, ich habe auf Anraten nicht den Timer1 wieder auf 0 gesetzt, 
sondern der läuft immer weiter.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es dürfte nicht sehr sinnvoll sein, die "Phasen" von zwei Sequenzen zu 
kombinieren. Der Ansatz, in 10 Werten eine vollständige Sequenz zu 
suchen, war da schon sinnvoller. Deine Methode, den Anfang zu suchen, 
ist allerdings problematisch. Um Genaueres zu sagen, wäre es 
erforderlich, endlich mal zu sehen, was für Werte da überhaupt in 
Array drin stecken. Also lese ein paar mehr Werte am Stück ein 
(mindestens 10, aber auch ruhig noch mehr), und übertrage sie dann in 
einer Schleife ohne irgendwelche weiteren Berechnungen. Zeige hier die 
Ausgabe.

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Die UART-Routine habe ich hier im Forum gefunden und muss sagen, dass
>die gut klappt. Habe mit meiner Drehzahlmessung keine Probleme bei der
>Ausgabe, verwende auch den RS485-Bus.
ISR(SIG_UART_RECV)           // UART Receive Interrupt wurde ausgelöst
{
  while ( !(UCSRA & (1<<RXC)) );  
  zeichen = UDR;          // den empfangenen Wert in 'zeichen' speichern
}


ist aber doppeltgemoppelt.
Wenn du schon in der ISR bist, ist das RXC-Bit auf jeden Fall gesetzt.
Die while-Schleife kannst du dir sparen.
  UCSRA|=(1<<TXC);          // vorangegangene Übertragung ist zu Ende
Du brichst von dir aus die Übertragung ab?
TXC solltest du auswerten (abfragen), um die RS485 wieder auf Empfang 
schalten zu können. Das Bit wird (vom Controller) gesetzt, sobald das 
letzte Bit den Puffer verlassen hat und kein neues im UDR steht.


Soviel zum Quelltext. Für dein Problem hat Stefan Ernst schon eine gute 
Lösug gebracht:
Schmeiss deine Auswertung erst mal raus (auskommentiere reicht).
Dann lässt du dir einfach jeden Wert ausgeben.
Da sollte man dann ein Muster erkennen.
Zumindest habe ich auf Weise schon diverse Modell-Fernsteuerungen 
ausgewertet (Multiswitch- und Multiprop-Module).
/PortB konfigurieren
sehr sinnfreier Kommentar, da man das schon am Namen des Ports erkennt. 
Wichtiger wäre die Angabe, welcher Pin wie (Richtung, High, low, Pullup) 
geschaltet ist.

statt des "Stop-Zeichens" solltest du lieber 0x0D (Carriage Return) 
ausgeben. Dann springt vermutlich auch das Terminalprogramm eine Zeile 
weiter.

Autor: Joe (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe nun folgendes geändert:
volatile unsigned int Array[10];    // uINT-Array [0-9]

ISR(TIMER1_CAPT_vect)
{
  Wcapture = ICR1;                // ICR1-Zeit nach Wcapture speichern
  Array[Bloopcounter] = Wcapture - Wcapturealt;  // Zeitdifferenz berechnen und im Array speichern
  Wcapturealt = Wcapture;              // aktuelle ICR1-Zeit wird alte Zeit zur Berechnung
  Bloopcounter++;                  // Zähler erhöhen

    if(Bloopcounter == 11)    // wenn bis 11 gezählt...
    {
    NeueDaten = TRUE;      // ...Job-Flag setzen...
    Bloopcounter = 1;      // ...Zähler auf 1 setzen...
    INTERRUPTaus;        // und Interrupts vom Timer ausschalten
    }
}

  while(1)
  {
  unsigned char I;
  if(NeueDaten)
    {
    for(I=0;I<11;I++)
      {
      Ergebnis = Array[I];
      ausgabe();        // Zeiten werden per UART ausgegeben
      }
    Wcapturealt = 0;      // alte Capture-Zeit auf 0 setzen
    TIFR  |= (1 << ICF1);    // Löschen des Interruptflags für ICR1
    INTERRUPTein;        // Interrupts vom Timer einschalten
    NeueDaten = FALSE;      // Job-Flag auf 0 setzen
    
    } // if(NeueDaten)

  _delay_ms(200);        // 200ms warten

  } // while(1)

Im Anhang siehst du die Daten. Array[0] wird nicht gefüllt, das ist 
schon mal richtig. Aber ich bekomme 11 Messdaten und nicht 10.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
STK500-Besitzer schrieb:
  UCSRA|=(1<<TXC);          // vorangegangene Übertragung ist zu Ende

> Du brichst von dir aus die Übertragung ab?

Nein, er löscht das Flag.

> TXC solltest du auswerten (abfragen), um die RS485 wieder auf Empfang
> schalten zu können. Das Bit wird (vom Controller) gesetzt, sobald das
> letzte Bit den Puffer verlassen hat und kein neues im UDR steht.

Eben, und um auf das Setzen durch die Hardware warten zu können, muss er 
das Flag ja erstmal selber löschen.


@ Joe:

Die Werte sehen für mich nach ziemlichen Müll aus. In welchem zeitlichen 
Rahmen spielt sich das ab? Bist du wirklich sicher, dass die Zeit 
zwischen 2 steigenden Flanken auf jeden Fall kleiner ist, wie der Timer 
für einen kompletten Durchlauf braucht? Konfiguriere den Timer mal mit 
einem Prescaler (sagen wir 8), und zeige dann bitte nochmal die Werte.
Bloopcounter = 1;
...
    for(I=0;I<11;I++)
Fülle das Array bitte von 0 an, und gib dann nur bis 9 (<10) aus. Du 
gibst momentan 11 Werte aus, von denen aber nur 8 verwertbar sind.

Autor: Joe (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Stefan schrieb:
> Bist du wirklich sicher, dass die Zeit zwischen 2 steigenden
> Flanken auf jeden Fall kleiner ist, wie der Timer
> für einen kompletten Durchlauf braucht?
Nein, sicher bin ich mir nicht, habe mich nur an den Basic-Code 
gehalten.

Ich habe das nun so in die Tat umgesetzt:
ISR(TIMER1_CAPT_vect)
{
  Wcapture = ICR1;                // ICR1-Zeit nach Wcapture speichern
  Array[Bloopcounter] = Wcapture - Wcapturealt;  // Zeitdifferenz berechnen und im Array speichern
  Wcapturealt = Wcapture;              // aktuelle ICR1-Zeit wird alte Zeit zur Berechnung
  Bloopcounter++;                  // Zähler erhöhen

    if(Bloopcounter == 10)    // wenn bis 11 gezählt...
    {
    NeueDaten = TRUE;      // ...Job-Flag setzen...
    Bloopcounter = 0;      // ...Zähler auf 1 setzen...
    INTERRUPTaus;        // und Interrupts vom Timer ausschalten
    }
}

.....

int main()
{
  TCCR1B = (1<<ICNC1) | (1<<ICES1)  | (1<<CS11); // Noise-Canceller, Input Capture Edge (pos. Flanke), Vorteiler: 8
  TIMSK = (1<<TICIE1) | (1<<TOIE1); // Interrupts aktivieren, Capture + Overflow

  Wcapturealt = 0;      // alte Capture-Zeit auf 0 setzen
  Bloopcounter = 0;      // Zähler auf 1 setzen
  NeueDaten = FALSE;      // Job-Flag auf 0 setzen

  ioinit();    // IOs initialisieren
  uartinit();    // UART initialisieren   
  sei();      // globale Interrupts zulassen



  while(1)
  {
  unsigned char I;
  if(NeueDaten)
    {
    for(I=0;I<10;I++)
      {
      Ergebnis = Array[I];
      ausgabe();        // Zeiten werden per UART ausgegeben
      }
      ausgabe_cr();     // Carriage Return per UART ausgeben
    Wcapturealt = 0;      // alte Capture-Zeit auf 0 setzen
    TIFR  |= (1 << ICF1);    // Löschen des Interruptflags für ICR1
    INTERRUPTein;        // Interrupts vom Timer einschalten
    NeueDaten = FALSE;      // Job-Flag auf 0 setzen
    
    } // if(NeueDaten)

  _delay_ms(200);        // 200ms warten

  } // while(1)

} // main

Nicht wundern, ich habe nach der 10ten Ausgabe ein CR gesetzt, damit es 
nun besser lesbar wird.

Im Anhang ist das Ergebnis zu sehen.

Autor: Joe (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Irgendwie scheinst du Recht zu haben, das hat was mit dem Vorteiler zu 
tun, denn wenn ich den Vorteiler auf 1024 stelle, kommt das raus, was du 
im Anhang sehen kannst (kleinere Zahlen sind übersichtlicher).
Ich finde aber noch keinen Bezug zum Zyklus vom UTI, der in der PDF der 
angehängten ZIP in einem von meinen letztens Posts zu sehen ist.

Der Wert von Array[0] kann ja nicht stimmen, insofern passt das schon 
mal.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Doch, das Muster schimmert durch. Z.B. die 2. Zeile, 82 + 83 sind Phase 
1, und dann sind 98 + 97 Phase 1 vom nächsten Zyklus. In manchen Zeilen 
sind die Werte aber so am schwanken, dass man nur raten kann, was da die 
Phase-1-Werte sind (z.B. in der Vorletzten). Ist am UTI überhaupt etwas 
angeschlossen?

Autor: Joe (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Stefan Ernst schrieb:
> Ist am UTI überhaupt etwas angeschlossen?
Ja, am UTI ist ein PT100 angeschlossen. Habe mal ein Anschlussbild davon 
hier angehängt. Ich habe aber nur ein PT100 mit zwei Anschlüssen, darum 
habe ich die Anschlüsse direkt am PT100 gebrückt. Also im Grunde B mit C 
und D mit F.
So steht das auch in der apputi03.pdf.

Habe probeweise das PT100 abgeklemmt und musste feststellen, dass sich 
an den Zeiten nichts ändert, nur die Ausgabe kommt verzögerter.
Schließe ich das PT100 wieder an, läuft auch die Ausgabe schneller.
Wenn ich _delay_ms(200); auskommentiere, bringt das auch nichts.

Meinst du, am UTI ist was faul?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joe schrieb:

> Meinst du, am UTI ist was faul?

<off topic>
Ich habe mein sowieso noch nie verstanden, wozu man dieses Teil 
überhaupt braucht. PTC direkt an den ADC ist doch viel simpler. Und wenn 
der Sensor tatsächlich weit, weit weg ist, dann spendier ich dem Sensor 
einen Tiny und überlge mir eine Übertragung per RS232/SPI/I2C/TWI oder 
was auch immer angemessen ist.
Aber das Rumgewurschtel mit einer Pulslängencodierung, bei der der 
kleinste Wert den Datensatz-Anfang markiert .... brrrr. Da läuft es mir 
kalt runter.

Kann natürlich auch sein, dass es ein extrem gutes Argument für so einen 
UTI gibt, das ich nicht sehe.
</off topic>

Autor: Joe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
<off topic>

Karl heinz Buchegger schrieb:
> Ich habe mein sowieso noch nie verstanden, wozu man dieses Teil
> überhaupt braucht. PTC direkt an den ADC [...] der kleinste Wert
> den Datensatz-Anfang markiert .... brrrr. Da läuft es mir
> kalt runter.
Ja, da stimme ich dir zu. Da ich aber ein fertig kalibriertes Bauteil 
einsetzen wollte, entschied ich mich für den UTI. Wollte auch mal sehen, 
wie das dann so in der Industrie umgesetzt wird, denn bei uns sind auch 
einige UTI's auf Steckplatinen verbaut.

Ich habe mich immer gewundert, warum die industrielle Messtechnik so 
teuer ist. Mittlerweile weiß ich es, denn wenn die Fehlerauswertung bei 
der Herstellerfirma genau so lange dauert, ist es klar, dass die 
Techniker auch bezahlt werden möchten, was den Preis natürlich 
unbezahlbar in die Höhe treibt! ironie

Karl heinz Buchegger schrieb:
> Kann natürlich auch sein, dass es ein extrem gutes Argument
> für so einen UTI gibt, das ich nicht sehe.
Ein Pro-Argument: Nur ein Bauteil, das fertig für die verschiedensten 
Messungen kalibiert ist.
Nachteil: Total besch**** Produktunterstützung, auch seitens des 
Herstellers. Hab mal nach einem C-Code für die AVR's gefragt, gibt's 
nicht.

</off topic>

Autor: Joe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Stefan:
Nun habe ich nochmals die Anschlüsse vom UTI kontrolliert und siehe da, 
es passt alles. Wenn ich nun die Widerstandswerte einzeln an den Pins 
des UTI messe, komme ich zu folgendem Ergebnis:

E nach A: 2,2kOhm
E nach B: 2,3kOhm
E nach C: 2,3kOhm
E nach D: 2,412kOhm
E nach F: 2,412kOhm
A nach B: 100Ohm
A nach D: 212Ohm
A nach F: 212Ohm
B nach D: 112Ohm
B nach F: 112Ohm
C nach D: 112Ohm
C nach F: 112Ohm

So, wenn ich das PT100 nicht anstöpsle, sind nur nur Anschlüsse E, A, B 
belegt.
Das PT100 hab ich direkt gebrückt (weil 2-Leiter) und nicht auf der 
Platine bzw. am UTI mit den Anschlüssen B-C und D-F.

Wenn ich mit meinem Widerstandsmessgerät direkt das PT100 auslese und 
dabei erwärme, steigt der Wert von 1120Ohm auf 114Ohm und höher. Lass 
ich das PT100 abkühlen, fällt auch der Widerstandswert.
Warum schafft das mein supergünstiges Widerstandsmessgerät vom großen C?

Ich habe mir auch mal das Ausganssignal vom UTI angeguckt. Wenn das 
PT100 nicht angeklemmt ist, kann man die zwei kürzesten Phasen erkennen, 
aber sobald das PT100 angeklemmt ist, kommt nur noch Mist raus.
Irgendwas muss doch mit dem UTI sein...

So langsam glaube ich, ich besorge mir eine kleine Konstantstromquelle 
und lese den widerstandsabhängigen Spannungswert vom PT100 ein... Mich 
kotzt das UTI-IC echt an.
Nebenbei bemerkt, beim PT100 muss es bleiben, das ist eine 
Voraussetzung, die ich mir gestellt habe. Schließlich habe ich auf der 
Arbeit genug mit diesen Temperaturelementen zu tun und möchte auch damit 
arbeiten.

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.