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


von Joe (Gast)


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

von Karl H. (kbuchegg)


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.

von Joe (Gast)


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?!

von Karl H. (kbuchegg)


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.

von Joe (Gast)


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...
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <stdlib.h>
4
#include <util/delay.h>
5
6
#ifndef F_CPU
7
#define F_CPU 3686400
8
#endif
9
10
#ifndef TRUE
11
#define TRUE 1
12
#endif
13
14
#ifndef FALSE
15
#define FALSE 0
16
#endif
17
18
#define INTERRUPTein   TIMSK |= (1<<TICIE1) | (1<<TOIE1);  // Makro für Capture-Interrupt einschalten
19
#define INTERRUPTaus   TIMSK &= ~((1<<TICIE1) | (1<<TOIE1)); // Makro für Capture-Interrupt ausschalten
20
21
char *Stopp = "#";    // Stoppzeichen für den String
22
char string[20];    // Array 'string' mit 20 Zeichen festlegen
23
24
volatile unsigned int Messung[5];
25
volatile unsigned int Ergebnis;
26
27
volatile unsigned int Wcapture;
28
volatile unsigned int Wcapturealt;
29
volatile unsigned int Bloopcounter;
30
31
volatile unsigned char  NeueDaten;     // Job Flag
32
33
//initialisieren der I/O-Schnittstellen
34
void ioinit (void)
35
{
36
  DDRB = 0b00000110;            //PortB konfigurieren
37
  PORTB = 0b11000011;            
38
  DDRD = 0b00000010;            //PortD konfigurieren
39
  PORTD = 0b00010100;               
40
}
41
42
void ausgabe (void)
43
{
44
  itoa(Ergebnis, string, 10);  // 'Ergebnis' nach ASCII wandeln
45
  uart_puts(string);    // 'ASCII-Ergebnis' ausgeben
46
  uart_puts(Stopp);    // Stoppzeichen ausgeben
47
48
}
49
50
ISR( TIMER1_CAPT_vect )
51
{
52
Wcapture = ICR1;
53
Messung[Bloopcounter] = Wcapture - Wcapturealt;
54
Wcapturealt = Wcapture;
55
Bloopcounter++;
56
57
if(Bloopcounter == 6)
58
{
59
NeueDaten = TRUE;
60
INTERRUPTaus;
61
}
62
}
63
64
int main()
65
{
66
  TCCR1B = (1<<ICNC1) | (1<<ICES1)  | (1<<CS10); // Noise-Canceller, Input Capture Edge (pos. Flanke), kein PreScale
67
  TIMSK = (1<<TICIE1) | (1<<TOIE1); // Interrupts aktivieren, Capture + Overflow
68
69
  Wcapturealt = 0;
70
  Bloopcounter = 0;
71
  NeueDaten = FALSE;
72
73
  ioinit();  
74
  sei();
75
76
  while(1)
77
  {
78
  if(NeueDaten){
79
80
  unsigned char I;
81
82
  for (I=0; I<5; I++)
83
  {
84
  Ergebnis = Messung[I];
85
  ausgabe();
86
  }
87
88
ICR1 = 0;
89
Wcapturealt = 0;
90
Bloopcounter = 0;
91
TIFR  |= (1 << ICF1);  // Löschen des Interruptflags für ICR1
92
INTERRUPTein;
93
NeueDaten = FALSE;
94
}
95
_delay_ms(200);
96
97
  } // while(1)
98
} // 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?

von H.j.Seifert (Gast)


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...

von Karl H. (kbuchegg)


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.

von Joe (Gast)


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.

von Karl H. (kbuchegg)


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.

von Joe (Gast)


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!

von Karl H. (kbuchegg)


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.

von Joe (Gast)


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.

von STK500-Besitzer (Gast)


Lesenswert?

>volatile unsigned int Messung[5];

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

von Joe (Gast)


Lesenswert?

Oh, hatte ich vergessen mitzuposten:
1
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?

von Stefan E. (sternst)


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".

von Joe (Gast)


Lesenswert?

Die Overflow-ISR ist noch drin:
1
ISR( TIMER1_OVF_vect )
2
{
3
  Overflows++;
4
}
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!

von Stefan E. (sternst)


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.

von Joe (Gast)


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?

von Stefan E. (sternst)


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)

von Joe (Gast)


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:
1
#define INTERRUPTein   TIMSK |= (1<<TICIE1) | (1<<TOIE1);  // Makro für Capture-Interrupt einschalten
2
#define INTERRUPTaus   TIMSK &= ~((1<<TICIE1) | (1<<TOIE1)); // Makro für Capture-Interrupt ausschalten
3
4
volatile double W1;
5
volatile double W2;
6
volatile double W3;
7
volatile unsigned int Phase1;
8
volatile unsigned int Phase2;
9
volatile unsigned int Phase3;
10
volatile unsigned int Messung[10];
11
12
void ausgabe (void)
13
{
14
  utoa((int)Ergebnis, string, 10); // (int) ist ein "cast" - 'Ergebnis' nach ASCII wandeln
15
  uart_puts(string);    // 'ASCII-Ergebnis' ausgeben
16
}
17
18
ISR( TIMER1_CAPT_vect )
19
{
20
Wcapture = ICR1;
21
Messung[Bloopcounter] = Wcapture - Wcapturealt;
22
Wcapturealt = Wcapture;
23
Bloopcounter++;
24
25
if(Bloopcounter == 10)
26
{
27
NeueDaten = TRUE;
28
INTERRUPTaus;
29
}
30
}
31
32
int main()
33
{
34
  TCCR1B = (1<<ICNC1) | (1<<ICES1)  | (1<<CS10); // Noise-Canceller, Input Capture Edge (pos. Flanke), kein PreScale
35
  TIMSK = (1<<TICIE1) | (1<<TOIE1); // Interrupts aktivieren, Capture + Overflow
36
37
  Wcapturealt = 0;
38
  Bloopcounter = 0;
39
  NeueDaten = FALSE;
40
41
  ioinit();  
42
  uartinit();   
43
44
  sei();
45
46
  while(1)
47
  {
48
  if(NeueDaten){
49
  cli();
50
51
unsigned char X, I;
52
53
X=2;
54
I=3;
55
56
X = 2;
57
58
 for (I=3;I<6;I++)
59
 {
60
 if( Messung[X] > Messung[I] )
61
 {
62
 X = I;
63
 }
64
 }
65
66
 if(X==2)
67
 {
68
 Phase1 = Messung[2] + Messung[3];
69
 Phase2 = Messung[4];
70
 Phase3 = Messung[5];
71
 }
72
 if(X==3)
73
 {
74
 Phase1 = Messung[3] + Messung[4];
75
 Phase2 = Messung[5];
76
 Phase3 = Messung[6];
77
 }
78
  if(X==4)
79
 {
80
 Phase1 = Messung[4] + Messung[5];
81
 Phase2 = Messung[6];
82
 Phase3 = Messung[7];
83
 }
84
  if(X==5)
85
 {
86
 Phase1 = Messung[5] + Messung[6];
87
 Phase2 = Messung[7];
88
 Phase3 = Messung[8];
89
 }
90
91
W1 = Phase3 - Phase1;
92
W2 = Phase2 - Phase1;
93
94
W1 = W1*100000;
95
W3 = W1/W2;
96
W3 = W3/32;
97
W3 = W3*334;
98
W3 = W3/100;
99
100
Ergebnis = W3;
101
ausgabe();
102
Wcapturealt = 0;
103
Bloopcounter = 0;
104
TIFR  |= (1 << ICF1);  // Löschen des Interruptflags für ICR1
105
INTERRUPTein;
106
NeueDaten = FALSE;
107
sei();
108
}

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.

von Karl H. (kbuchegg)


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.

von STK500-Besitzer (Gast)


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.

von Joe (Gast)


Angehängte Dateien:

Lesenswert?

So, anbei nun mein kompletter Code:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <stdlib.h>
4
#include <util/delay.h>
5
6
#ifndef F_CPU
7
#define F_CPU 3686400
8
#endif
9
10
#ifndef TRUE
11
#define TRUE 1
12
#endif
13
14
#ifndef FALSE
15
#define FALSE 0
16
#endif
17
18
#define BAUD 9600            // Baudrate
19
#define USARTSPEED (F_CPU/(BAUD*16L)-1)  // Formel zur Berechnung der Werte für UBBRH/UBBRL
20
#define LOW(x)  ((x) & 0xFF)      // Makro zum Lowbyte Zugriff eines Word
21
#define HIGH(x) (((x)  >> 8) & 0xFF)  // Makro zum Highbyte Zugriff eines Word
22
#define RS485RX  PORTD &= ~(1<<PD2)    // Makro für MAX487 als Empfänger
23
#define RS485TX  PORTD |= (1<<PD2)    // Makro für MAX487 als Sender
24
#define INTERRUPTein   TIMSK |=   (1<<TICIE1) | (1<<TOIE1);  // Makro für Capture-Interrupt einschalten
25
#define INTERRUPTaus   TIMSK &= ~((1<<TICIE1) | (1<<TOIE1)); // Makro für Capture-Interrupt ausschalten
26
27
char *Stop = "#";    // Stoppzeichen für den String
28
char string[20];    // Array 'string' mit 20 Zeichen festlegen
29
30
volatile uint8_t zeichen;        // zeichen als unsigned integer
31
volatile double Ergebnis = 0.0;      // Ergebnis als global deklarieren
32
volatile unsigned int Wcapture;      // Wcapture als unsigned int
33
volatile unsigned int Wcapturealt;    // Wcapturealt als unsigned int
34
volatile unsigned int Array[6];      // uINT-Array [0-5]
35
volatile unsigned char Bloopcounter;  // Impulszähler
36
volatile double W1;            // erster Wert zur Berechnung
37
volatile double W2;            // zweiter Wert zur Berechnung
38
volatile double W3;            // Ergebnis von der Berechnung
39
volatile unsigned int Phase1;      // Zeitwert für Formel
40
volatile unsigned int Phase2;      // Zeitwert für Formel
41
volatile unsigned int Phase3;      // Zeitwert für Formel
42
volatile unsigned char NeueDaten;    // Job-Flag
43
volatile unsigned char Overflows = 0;   // Anzahl der Timer Overflows die während
44
                                         // der Messung passiert sind
45
46
ISR(SIG_UART_RECV)           // UART Receive Interrupt wurde ausgelöst
47
{
48
  while ( !(UCSRA & (1<<RXC)) );  
49
  zeichen = UDR;          // den empfangenen Wert in 'zeichen' speichern
50
}
51
52
void UART_SendByte(uint8_t data)  // sendet ein Byte über das UART
53
{
54
  while (!(UCSRA & (1<<UDRE)));
55
  UCSRA|=(1<<TXC);          // vorangegangene Übertragung ist zu Ende
56
  UDR = data;            // 'data' ausgeben
57
}
58
59
void uart_puts(char *s)
60
{  
61
  RS485TX;                       // Sender an
62
  while (*s)
63
  {
64
    UART_SendByte(*s++);        // Daten senden 
65
  }
66
  while (!(UCSRA & (1<<TXC)));   // Warten, bis Senden komplett
67
  RS485RX;                       // Sender aus
68
  zeichen=0;
69
}
70
71
72
void uartinit (void)
73
{
74
  UBRRH = HIGH(USARTSPEED);            // Baudrate einstellen
75
  UBRRL = LOW(USARTSPEED);            // Baudrate einstellen
76
  UCSRB = (1<<TXEN) | (1<<RXEN) | (1<<RXCIE);  // Senden, Empfangen, ReceiveInt aktivieren
77
  UCSRC = (1<<URSEL)|(3<<UCSZ0);        // Frame Format setzen:8data, 1stop bit (URSEL=1 -> UCSRC->Settings werden genutzt)
78
}
79
80
81
void ioinit (void)
82
{
83
  DDRB  = 0b00000110;            //PortB konfigurieren
84
  PORTB = 0b11000011;            
85
  DDRD  = 0b00000010;            //PortD konfigurieren
86
  PORTD = 0b00010100;               
87
}
88
89
void ausgabe (void)
90
{
91
  utoa(Ergebnis, string, 10);  //'Ergebnis' nach ASCII wandeln
92
  uart_puts(string);      // 'ASCII-Ergebnis' ausgeben
93
  uart_puts(Stop);        // Stoppzeichen ausgeben
94
}
95
96
ISR(TIMER1_CAPT_vect)
97
{
98
  Wcapture = ICR1;                // ICR1-Zeit nach Wcapture speichern
99
  Array[Bloopcounter] = Wcapture - Wcapturealt;  // Zeitdifferenz berechnen und im Array speichern
100
  Wcapturealt = Wcapture;              // aktuelle ICR1-Zeit wird alte Zeit zur Berechnung
101
  Bloopcounter++;                  // Zähler erhöhen
102
103
    if(Bloopcounter == 6)    // wenn bis 6 gezählt...
104
    {
105
    NeueDaten = TRUE;      // ...Job-Flag setzen...
106
    Bloopcounter = 1;      // ...Zähler auf 1 setzen...
107
    INTERRUPTaus;        // und Interrupts vom Timer ausschalten
108
    }
109
}
110
111
112
ISR( TIMER1_OVF_vect )
113
{
114
  Overflows++;
115
}
116
117
118
int main()
119
{
120
  TCCR1B = (1<<ICNC1) | (1<<ICES1)  | (1<<CS10); // Noise-Canceller, Input Capture Edge (pos. Flanke), kein PreScale
121
  TIMSK = (1<<TICIE1) | (1<<TOIE1); // Interrupts aktivieren, Capture + Overflow
122
123
  Wcapturealt = 0;      // alte Capture-Zeit auf 0 setzen
124
  Bloopcounter = 1;      // Zähler auf 1 setzen
125
  NeueDaten = FALSE;      // Job-Flag auf 0 setzen
126
127
  ioinit();    // IOs initialisieren
128
  uartinit();    // UART initialisieren   
129
  sei();      // globale Interrupts zulassen
130
131
132
133
  while(1)
134
  {
135
  if(NeueDaten)
136
    {
137
    unsigned char X, I;
138
139
    X=2;
140
141
     for (I=3;I<6;I++)  // die zwei kürzesten Zeiten finden
142
       {
143
        if( (Array[X]) > (Array[I]) )
144
          {
145
          X = I;
146
          }
147
       }
148
149
     if(X==2)
150
     {
151
     Phase1 = Array[2] + Array[3];  // die zwei kürzesten Zeiten addieren
152
     Phase2 = Array[4];
153
     Phase3 = Array[5];
154
     }
155
   
156
     if(X==3)
157
     {
158
     Phase1 = Array[3] + Array[4];  // die zwei kürzesten Zeiten addieren
159
     Phase2 = Array[5];
160
     Phase3 = Array[2];
161
     }
162
163
     if(X==4)
164
     {
165
     Phase1 = Array[4] + Array[5];  // die zwei kürzesten Zeiten addieren
166
     Phase2 = Array[2];
167
     Phase3 = Array[3];
168
     }
169
170
     if(X==5)
171
     {
172
     Phase1 = Array[5] + Array[2];  // die zwei kürzesten Zeiten addieren
173
     Phase2 = Array[3];
174
     Phase3 = Array[4];
175
     }
176
177
    W1 = Phase3 - Phase1;    // Berechnungen durchführen [Start]
178
    W2 = Phase2 - Phase1;
179
180
    W1 = W1*100000;
181
    W3 = W1/W2;
182
    W3 = W3/32;
183
    W3 = W3*334;
184
    W3 = W3/100;        // Berechnungen durchführen [Ende] 
185
186
    Ergebnis = W3;        // Ergebnis der Berechnung ist W3 als double
187
    ausgabe();          // W3 wird per UART ausgegeben (als Ergebnis)
188
    Wcapturealt = 0;      // alte Capture-Zeit auf 0 setzen
189
    TIFR  |= (1 << ICF1);    // Löschen des Interruptflags für ICR1
190
    INTERRUPTein;        // Interrupts vom Timer einschalten
191
    NeueDaten = FALSE;      // Job-Flag auf 0 setzen
192
    
193
    } // if(NeueDaten)
194
195
  _delay_ms(200);        // 200ms warten
196
197
  } // while(1)
198
199
} // 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.

von Stefan E. (sternst)


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.

von STK500-Besitzer (Gast)


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.
1
ISR(SIG_UART_RECV)           // UART Receive Interrupt wurde ausgelöst
2
{
3
  while ( !(UCSRA & (1<<RXC)) );  
4
  zeichen = UDR;          // den empfangenen Wert in 'zeichen' speichern
5
}

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.
1
  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).
1
/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.

von Joe (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe nun folgendes geändert:
1
volatile unsigned int Array[10];    // uINT-Array [0-9]
2
3
ISR(TIMER1_CAPT_vect)
4
{
5
  Wcapture = ICR1;                // ICR1-Zeit nach Wcapture speichern
6
  Array[Bloopcounter] = Wcapture - Wcapturealt;  // Zeitdifferenz berechnen und im Array speichern
7
  Wcapturealt = Wcapture;              // aktuelle ICR1-Zeit wird alte Zeit zur Berechnung
8
  Bloopcounter++;                  // Zähler erhöhen
9
10
    if(Bloopcounter == 11)    // wenn bis 11 gezählt...
11
    {
12
    NeueDaten = TRUE;      // ...Job-Flag setzen...
13
    Bloopcounter = 1;      // ...Zähler auf 1 setzen...
14
    INTERRUPTaus;        // und Interrupts vom Timer ausschalten
15
    }
16
}
17
18
  while(1)
19
  {
20
  unsigned char I;
21
  if(NeueDaten)
22
    {
23
    for(I=0;I<11;I++)
24
      {
25
      Ergebnis = Array[I];
26
      ausgabe();        // Zeiten werden per UART ausgegeben
27
      }
28
    Wcapturealt = 0;      // alte Capture-Zeit auf 0 setzen
29
    TIFR  |= (1 << ICF1);    // Löschen des Interruptflags für ICR1
30
    INTERRUPTein;        // Interrupts vom Timer einschalten
31
    NeueDaten = FALSE;      // Job-Flag auf 0 setzen
32
    
33
    } // if(NeueDaten)
34
35
  _delay_ms(200);        // 200ms warten
36
37
  } // 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.

von Stefan E. (sternst)


Lesenswert?

STK500-Besitzer schrieb:
1
  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.
1
Bloopcounter = 1;
2
...
3
    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.

von Joe (Gast)


Angehängte Dateien:

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:
1
ISR(TIMER1_CAPT_vect)
2
{
3
  Wcapture = ICR1;                // ICR1-Zeit nach Wcapture speichern
4
  Array[Bloopcounter] = Wcapture - Wcapturealt;  // Zeitdifferenz berechnen und im Array speichern
5
  Wcapturealt = Wcapture;              // aktuelle ICR1-Zeit wird alte Zeit zur Berechnung
6
  Bloopcounter++;                  // Zähler erhöhen
7
8
    if(Bloopcounter == 10)    // wenn bis 11 gezählt...
9
    {
10
    NeueDaten = TRUE;      // ...Job-Flag setzen...
11
    Bloopcounter = 0;      // ...Zähler auf 1 setzen...
12
    INTERRUPTaus;        // und Interrupts vom Timer ausschalten
13
    }
14
}
15
16
.....
17
18
int main()
19
{
20
  TCCR1B = (1<<ICNC1) | (1<<ICES1)  | (1<<CS11); // Noise-Canceller, Input Capture Edge (pos. Flanke), Vorteiler: 8
21
  TIMSK = (1<<TICIE1) | (1<<TOIE1); // Interrupts aktivieren, Capture + Overflow
22
23
  Wcapturealt = 0;      // alte Capture-Zeit auf 0 setzen
24
  Bloopcounter = 0;      // Zähler auf 1 setzen
25
  NeueDaten = FALSE;      // Job-Flag auf 0 setzen
26
27
  ioinit();    // IOs initialisieren
28
  uartinit();    // UART initialisieren   
29
  sei();      // globale Interrupts zulassen
30
31
32
33
  while(1)
34
  {
35
  unsigned char I;
36
  if(NeueDaten)
37
    {
38
    for(I=0;I<10;I++)
39
      {
40
      Ergebnis = Array[I];
41
      ausgabe();        // Zeiten werden per UART ausgegeben
42
      }
43
      ausgabe_cr();     // Carriage Return per UART ausgeben
44
    Wcapturealt = 0;      // alte Capture-Zeit auf 0 setzen
45
    TIFR  |= (1 << ICF1);    // Löschen des Interruptflags für ICR1
46
    INTERRUPTein;        // Interrupts vom Timer einschalten
47
    NeueDaten = FALSE;      // Job-Flag auf 0 setzen
48
    
49
    } // if(NeueDaten)
50
51
  _delay_ms(200);        // 200ms warten
52
53
  } // while(1)
54
55
} // 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.

von Joe (Gast)


Angehängte Dateien:

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.

von Stefan E. (sternst)


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?

von Joe (Gast)


Angehängte Dateien:

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?

von Karl H. (kbuchegg)


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>

von Joe (Gast)


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>

von Joe (Gast)


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.

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.