Forum: Mikrocontroller und Digitale Elektronik Encoder Tutorial


von Michael H. (h_m)


Angehängte Dateien:

Lesenswert?

Hallo,
ich möchte gerne das Tutorial hier aus dem Forum über Drehencoder 
Testen, allerdings mit einem ATmega328P 16MHz.

jetzt habe ich in der Funktion „encode_init“  die Timer Register 
angepasst auf den 328p. Und möchte die Funktion encode_read2  nutzen.
ich benutze einen 2-bit encoder (bild)

aber irgenwie funktioniert das ganze nicht so recht, könnte mir hier 
bitte jemand weiterhelfen??

Meine Vermutung ist das ich den Timer nicht richtig initialisiert habe, 
aber ich komme nicht drauf.

[[https://www.mikrocontroller.net/articles/Drehgeber]]

so ist es aktuell drauf, keine Funktion +Bild
1
 /************************************************************************/
2
/*                                                                      */
3
/*                      Reading rotary encoder                      */
4
/*                      one, two and four step encoders supported   */
5
/*                                                                      */
6
/*              Author: Peter Dannegger                                 */
7
/*                                                                      */
8
/************************************************************************/
9
#include <avr/io.h>
10
#include <avr/interrupt.h>
11
12
// target: ATmega16
13
//------------------------------------------------------------------------
14
15
#define XTAL        16e6         // 8MHz
16
17
#define PHASE_A     (PIND & 1<<PD2)
18
#define PHASE_B     (PIND & 1<<PD3)
19
20
#define LEDS_DDR    DDRC
21
#define LEDS        PORTC           // LEDs against VCC
22
23
24
volatile int8_t enc_delta;          // -128 ... 127
25
static int8_t last;
26
27
28
void encode_init( void )
29
{
30
  int8_t new;
31
32
  new = 0;
33
  if( PHASE_A ) new = 3;
34
  if( PHASE_B ) new ^= 1;       // convert gray to binary
35
  last = new;                   // power on state
36
  enc_delta = 0;
37
  TCCR1B = (1<<WGM12) | (1<<CS11) | (1<<CS10);   // CTC, prescaler 64
38
  OCR1A = (uint8_t)(XTAL / 64.0 * 1e-3 - 0.5);   // 1ms
39
  TIMSK1 |= 1<<OCIE1A;
40
}
41
42
43
ISR( TIMER0_COMP_vect )            // 1ms for manual movement
44
{
45
  int8_t new, diff;
46
47
  new = 0;
48
  if( PHASE_A ) new = 3;
49
  if( PHASE_B ) new ^= 1;          // convert gray to binary
50
  diff = last - new;               // difference last - new
51
  if( diff & 1 ) {                 // bit 0 = value (1)
52
    last = new;                    // store new as next last
53
    enc_delta += (diff & 2) - 1;   // bit 1 = direction (+/-)
54
  }
55
}
56
57
58
int8_t encode_read1( void )         // read single step encoders
59
{
60
  int8_t val;
61
62
  cli();
63
  val = enc_delta;
64
  enc_delta = 0;
65
  sei();
66
  return val;                   // counts since last call
67
}
68
69
70
int8_t encode_read2( void )         // read two step encoders
71
{
72
  int8_t val;
73
74
  cli();
75
  val = enc_delta;
76
  enc_delta = val & 1;
77
  sei();
78
  return val >> 1;
79
}
80
81
82
int8_t encode_read4( void )         // read four step encoders
83
{
84
  int8_t val;
85
86
  cli();
87
  val = enc_delta;
88
  enc_delta = val & 3;
89
  sei();
90
  return val >> 2;
91
}
92
93
94
int main( void )
95
{
96
  int32_t val = 0;
97
98
  LEDS_DDR = 0xFF;
99
  encode_init();
100
  sei();
101
102
  for(;;){
103
    val += encode_read2();          // read a single step encoder
104
    LEDS = val;
105
  }
106
}

von Thomas E. (thomase)


Lesenswert?

Michael H. schrieb:
> TCCR1B = (1<<WGM12) | (1<<CS11) | (1<<CS10);   // CTC, prescaler 64
>   OCR1A = (uint8_t)(XTAL / 64.0 * 1e-3 - 0.5);   // 1ms
>   TIMSK1 |= 1<<OCIE1A;
> }
>
> ISR( TIMER0_COMP_vect )            // 1ms for manual movement
> {

Erstens heißt es COMPA und zweitens ist diese ISR dann für welchen 
Timer?

von Michael H. (h_m)


Angehängte Dateien:

Lesenswert?

Thomas E. schrieb:
> Michael H. schrieb:
>> TCCR1B = (1<<WGM12) | (1<<CS11) | (1<<CS10);   // CTC, prescaler 64
>>   OCR1A = (uint8_t)(XTAL / 64.0 * 1e-3 - 0.5);   // 1ms
>>   TIMSK1 |= 1<<OCIE1A;
>> }
>>
>> ISR( TIMER0_COMP_vect )            // 1ms for manual movement
>> {
>
> Erstens heißt es COMPA und zweitens ist diese ISR dann für welchen
> Timer?

Danke, das stimmt habe ich geändert das war noch nicht der Fehler.


ich möchte gerne das Tutorial hier aus dem Forum über Drehencoder
Testen, allerdings mit einem ATmega328P 16MHz.

jetzt habe ich in der Funktion „encode_init“  die Timer Register
angepasst auf den 328p. Und möchte die Funktion encode_read2  nutzen.
ich benutze einen 2-bit encoder (bild)

aber irgenwie funktioniert das ganze nicht so recht, könnte mir hier
bitte jemand weiterhelfen??

Meine Vermutung ist das ich den Timer nicht richtig initialisiert habe,
aber ich komme nicht drauf.

[[https://www.mikrocontroller.net/articles/Drehgeber]]



1
/************************************************************************/
2
/*                                                                      */
3
/*                      Reading rotary encoder                      */
4
/*                      one, two and four step encoders supported   */
5
/*                                                                      */
6
/*              Author: Peter Dannegger                                 */
7
/*                                                                      */
8
/************************************************************************/
9
#include <avr/io.h>
10
#include <avr/interrupt.h>
11
12
// target: ATmega16
13
//------------------------------------------------------------------------
14
15
#define XTAL        16e6         // 16MHz
16
17
#define PHASE_A     (PIND & 1<<PD2)
18
#define PHASE_B     (PIND & 1<<PD3)
19
20
#define LEDS_DDR    DDRC
21
#define LEDS        PORTC           // LEDs against VCC
22
23
24
volatile int8_t enc_delta;          // -128 ... 127
25
static int8_t last;
26
27
28
void encode_init( void )
29
{
30
  int8_t new;
31
32
  new = 0;
33
  if( PHASE_A ) new = 3;
34
  if( PHASE_B ) new ^= 1;       // convert gray to binary
35
  last = new;                   // power on state
36
  enc_delta = 0;
37
  TCCR1B = (1<<WGM12) | (1<<CS11) | (1<<CS10);   // CTC, prescaler 64
38
  OCR1A = (uint8_t)(XTAL / 64.0 * 1e-3 - 0.5);   // 1ms
39
  TIMSK1 |= 1<<OCIE1A;
40
}
41
42
43
ISR( TIMER1_COMPA_vect )            // 1ms for manual movement
44
{
45
  int8_t new, diff;
46
47
  new = 0;
48
  if( PHASE_A ) new = 3;
49
  if( PHASE_B ) new ^= 1;          // convert gray to binary
50
  diff = last - new;               // difference last - new
51
  if( diff & 1 ) {                 // bit 0 = value (1)
52
    last = new;                    // store new as next last
53
    enc_delta += (diff & 2) - 1;   // bit 1 = direction (+/-)
54
  }
55
}
56
57
58
int8_t encode_read1( void )         // read single step encoders
59
{
60
  int8_t val;
61
62
  cli();
63
  val = enc_delta;
64
  enc_delta = 0;
65
  sei();
66
  return val;                   // counts since last call
67
}
68
69
70
int8_t encode_read2( void )         // read two step encoders
71
{
72
  int8_t val;
73
74
  cli();
75
  val = enc_delta;
76
  enc_delta = val & 1;
77
  sei();
78
  return val >> 1;
79
}
80
81
82
int8_t encode_read4( void )         // read four step encoders
83
{
84
  int8_t val;
85
86
  cli();
87
  val = enc_delta;
88
  enc_delta = val & 3;
89
  sei();
90
  return val >> 2;
91
}
92
93
94
int main( void )
95
{
96
  int32_t val = 0;
97
98
  LEDS_DDR = 0xFF;
99
  encode_init();
100
  sei();
101
102
  for(;;){
103
    val += encode_read2();          // read a single step encoder
104
    LEDS = val;
105
  }
106
}

von Michael H. (h_m)


Lesenswert?

ich muß jetzt noch einmal nachfragen, ich komme mit der Rechnung so 
nicht klar.
1
TCCR1B = (1<<WGM12) | (1<<CS12) ;   // CTC, prescaler 256
2
  OCR1A = (uint8_t)(XTAL / 256.0 * 1e-3 - 0.5);   // 1ms
3
  TIMSK1 |= 1<<OCIE1A;


normalerweise Rechne ich den Vergleichswert. Taktfrequenz des 
Controllers / den Vorteiler * der gewüschten Zeit -1.

Das wären ja 16000000/256 = 62500 * 0.001 (1ms) = 62,5 - 1 = 61,5

also Vergleichswert 65535- 61,5

wie kann ich das jetzt in dem Makro umsetzen, da habe ich ein Brett vorm 
Kopf,

das müsste doch so stimmen ? dann müsste ja der ganze Code so 
funktioniernen oder ?
1
TCCR1B = (1<<WGM12) | (1<<CS12) ;   // CTC, prescaler 256
2
  OCR1A = (uint8_t)(XTAL / 256.0 * 1e-3 - 0.5);   // 1ms
3
  TIMSK1 |= 1<<OCIE1A;

von Thomas E. (thomase)


Lesenswert?

Michael H. schrieb:
> also Vergleichswert 65535- 61,5

Wie kommst du darauf?
1
TCCR1B = (1<<WGM12) | (1<<CS12) ;   // CTC, prescaler 256
2
  OCR1A = (uint8_t)(XTAL / 256.0 * 1e-3 - 0.5);   // 1ms
3
  TIMSK1 |= 1<<OCIE1A;

Das passt schon.

Dein Timer läuft mit dem eingestellten Prescaler mit 62,5 KHz. Wenn du 
bis 62,5 zählst, ist eine ms um.

Bis 62,5 kann der Timer aber nicht zählen, sondern bis 62 oder bis 63.

Die -0,5 dient zum Runden nach den bekannten Regeln.

: Bearbeitet durch User
von Michael H. (h_m)


Lesenswert?

OK, war mir total unsicher weil nichts leuchtet, jetzt frag ich mal 
sicherheitshalber nocheinmal etwas zur Funktion und Verdrahtung.

also, wenn das ganze funktioniert wird der der Zählwert Binär an PORTC 
ausgegeben. dh. je nach Wert müssten die LEDS von PC0-PC7 leuchten ?

ich habe an PORTC Die ANODE (+) und an GND eine gemeinsame Kathode 
angeschlossen. // LEDs against VCC richtig

von dem Encoder habe ich den common pin an +5V gelegt. dh. beim 
Zählimpuls gibt er Highpegel aus.

von Thomas E. (thomase)


Lesenswert?

Michael H. schrieb:
> von dem Encoder habe ich den common pin an +5V gelegt. dh. beim
> Zählimpuls gibt er Highpegel aus

Bei Eingängen, ob für Taster oder Encoder, wird üblicherweise über einen 
Pullup-Widerstand, meistens intern, Vcc angelegt und der Taster oder 
Encoder schaltet gegen GND. Üblicherweise heißt: zu >99,9%. Das wird von 
der Dannegger-Auswertung auch so erwartet.

So, wie du das machst, schaltet der Eingang zwischen Nichts und Vcc. 
Aber "Nichts" ist nicht GND oder Null. Deswegen kann das auch nicht 
funktionieren.

: Bearbeitet durch User
von Michael H. (h_m)


Lesenswert?

Achso, dann sind die Spuren A und B LOW_AKTIVE

von Thomas E. (thomase)


Lesenswert?

Michael H. schrieb:
> Achso, dann sind die Spuren A und B LOW_AKTIVE

Genau, das ist die übliche Vorgehensweise.

von Peter D. (peda)


Lesenswert?

Michael H. schrieb:
> Achso, dann sind die Spuren A und B LOW_AKTIVE

Das ist egal, nur floaten dürfen sie nicht.

von c-hater (Gast)


Lesenswert?

Peter D. schrieb:

> Michael H. schrieb:
>> Achso, dann sind die Spuren A und B LOW_AKTIVE
>
> Das ist egal

Richtig, nicht einmal die Zählrichtung sollte sich dadurch ändern.

> nur floaten dürfen sie nicht.

Das sollten Eingänge sowieso besser nie tun, benutzte auf jeden Fall 
garnicht.

von Michael H. (h_m)


Lesenswert?

Also Danke für die Hilfe,

jetzt habe ich noch eine Frage und zwar ist am schluß eine Auswertung 
mit diskreten Bauteilen.

und zwar habe ich mir das auch schon eimal gebaut. da kommt ja der 
Zählimpuls ich sag jetzt mal 4-fach raus.

kennt sich jemand aus, wie man das auf zb. 8-fach mit Bauteilen 
erweitern kann ?

von Udo S. (Firma: allround) (1udo1)


Lesenswert?

Hallo,

ich möchte den Wert von "val" abfragen, um dann die LED an PD7 zu 
toggeln. Meine konkrete Frage ist in der main als Kommentar eingebunden.
1
/* Tutorial Encoder Dannegger 
2
3
Der Encoder gibt die Flanken der Lichtschranken an PD0 und PD1. An PORT C werden die Werte von „val“ richtig angezeigt. Ebenfalls am LCD. Alles o.k.
4
5
Möchte bei einem bestimmten Wert von „val“ Pin PD7 togglen. Der Code funktioniert nicht. Wie muss die Abfrage in main aussehen?
6
7
*/
8
9
#include <avr/io.h>
10
#include <avr/interrupt.h>
11
#define F_CPU  11059200
12
#include <util/delay.h>
13
#include <stdlib.h>
14
#include <stdio.h>
15
#include "lcd-routines.h"
16
17
char Buffer[15];
18
19
void set(void)
20
{
21
  DDRA  |=0xff;    // Ausgang  PORTA
22
  PORTA |=  0x00;
23
24
  DDRB  |=0xff;    // Ausgang  PORTB
25
  PORTB |=  0xff;
26
27
  DDRC  |= 0xff;
28
  PORTC |=   0xff;
29
  
30
  DDRD |= 0xf0;    // PD0, PD1, INT0 PD2 und INT1 PD3 Eingänge
31
  PORTD |=  0xff;    //  Pullup-activated, --- Activ LOW
32
}
33
34
#define XTAL        11059200         // 11,059200MHZ
35
36
#define PHASE_A     (PIND & 1<<PD0)
37
#define PHASE_B     (PIND & 1<<PD1)
38
39
#define LEDS_DDR    DDRC
40
#define LEDS        PORTC           // LEDs against VCC
41
42
volatile int8_t enc_delta;          // -128 ... 127
43
static int8_t last;
44
45
void encode_init( void )
46
{
47
  int8_t new;
48
49
  new = 0;
50
  if( PHASE_A ) new = 3;
51
  if( PHASE_B ) new ^= 1;       // convert gray to binary
52
  last = new;                   // power on state
53
  enc_delta = 0;
54
  TCCR0 = (1<<WGM01) | (1<<CS01) | (1<<CS00);   // CTC, prescaler 64
55
  OCR0 =   (uint16_t)(XTAL / 64.0 * 1e-3 - 0.5);   // 1ms
56
  TIMSK |= 1<<OCIE0;
57
}
58
59
60
61
62
ISR( TIMER0_COMP_vect )            // 1ms for manual movement
63
{
64
  int8_t new, diff;
65
66
  new = 0;
67
  if( PHASE_A ) new = 3;
68
  if( PHASE_B ) new ^= 1;          // convert gray to binary
69
  diff = last - new;               // difference last - new
70
  if( diff & 1 )
71
  {                 // bit 0 = value (1)
72
    last = new;                    // store new as next last
73
    enc_delta += (diff & 2) - 1;   // bit 1 = direction (+/-)
74
  }
75
}
76
77
78
int8_t encode_read1( void )         // read single step encoders
79
{
80
  int8_t val;
81
82
  cli();
83
  val = enc_delta;
84
  enc_delta = 0;
85
  sei();
86
  return val;                   // counts since last call
87
}
88
89
90
int main( void )
91
{
92
  int16_t val = 0;
93
94
  LEDS_DDR = 0xFF;
95
  encode_init();
96
  lcd_init();
97
  sei();
98
99
  for(;;)
100
  {
101
    val += encode_read1();      // read a single step encoder
102
    LEDS = val;
103
    
104
      sprintf(Buffer, "Value = %6d", val);
105
      lcd_setcursor(0,1);
106
      lcd_string( Buffer );
107
      
108
// ####### Diese if-Abfrage möchte ich hier einsetzen, funktioniert aber nicht. ###########################
109
// PD7 soll toggeln, wenn die Achse des Motors eine bestimmte Anzahl von Umdrehungen durchgeführt hat
110
111
  if(val>=100)   // Ist bestimmt falscher Code. 
112
        {
113
  PORTD ^= ( 1 << PD7 );
114
        }
115
116
// ####### Diese Abfrage möchte ich hier einsetzen, funktioniert aber nicht mit diesem Code ##############
117
118
    
119
  }// Ende for
120
121
  return 0;
122
123
}// Ende main

von M. K. (sylaina)


Lesenswert?

Udo S. schrieb:
> DDRD |= 0xf0;    // PD0, PD1, INT0 PD2 und INT1 PD3 Eingänge
Kann man so schreiben, übersichtlicher fände ich:
1
DDRD |= (1 << PD7)|(1 << PD6)|(1 << PD5)|(1 << PD4);
Bei deiner Konfig geht auch 0xf0, der erfahrene Programmierer sieht da 
das Hight-Nibble drin. Nur als kleiner Tipp ;).
Udo S. schrieb:
> PORTD |=  0xff;    //  Pullup-activated, --- Activ LOW
Hiermit schaltest du nicht nur die Pullups ein, alle Ausgänge machst du 
damit auch scharf. Ich würde nur da Pullups einschalten wo ich sie auch 
definiert haben will.
Udo S. schrieb:
> #define XTAL        11059200         // 11,059200MHZ
Setz hier noch ein UL hinter die 11059200, der Compiler kann (muss 
nicht) auf dumme Ideen kommen, mit einem UL zwingt man ihn entsprechende 
Berechnungen im Code später als unsigned long zu bewerten.

An deinem Code ansich sehe ich erstmal keinen Fehler, eigentlich sollte 
das gehen wenn du am Encoder drehst. Aber 100 ist schon was, versuchs 
doch erstmal mit 5 oder so ;)

von Udo S. (Firma: allround) (1udo1)


Lesenswert?

Hallo sylaina,

Danke für Deine Antwort. Ich habe den val-Wert mal auf "==10" 
eingestellt. PORT C zeigt den Wert 0b0000 1010 an, also 10 . Auch mein 
LCD zeigt "10" an. Trotzdem wird PD7 nicht angesprochen. Die Ports sind 
in Ordnung. Im Testprogramm werden alle LEDS aller Ports ordnungsgemäß 
angesteuert.

Die Variable "val" ist doch integer, sodaß die if-Abfrage genau dieses 
Register abfragt, oder ist da was falsch?

1
for(;;)
2
  {
3
    val += encode_read1();      // read a single step encoder
4
    LEDS = val;
5
    
6
      sprintf(Buffer, "Value = %6d", val);
7
      lcd_setcursor(0,1);
8
      lcd_string( Buffer );
9
      
10
// ####### Diese if-Abfrage möchte ich hier einsetzen, funktioniert aber nicht. ###########################
11
// PD7 soll toggeln, wenn die Achse des Motors eine bestimmte Anzahl von Umdrehungen durchgeführt hat
12
13
  if(val==10)   // Ist diese Abfrage von der Syntax falsch? 
14
        {
15
         PORTD ^= ( 1 << PD7 );
16
        }

von Udo S. (Firma: allround) (1udo1)


Lesenswert?

Der Vollständigkeit halber die komplette for-Schleife inclusiv Klammern

[c]
for(;;)
  {
    val += encode_read1();      // read a single step encoder
    LEDS = val;

      sprintf(Buffer, "Value = %6d", val);
      lcd_setcursor(0,1);
      lcd_string( Buffer );

// ####### Diese if-Abfrage möchte ich hier einsetzen, funktioniert aber 
nicht. ###########################
// PD7 soll toggeln, wenn die Achse des Motors eine bestimmte Anzahl von 
Umdrehungen durchgeführt hat


if(val==10)   // vielleicht falsche Syntax?.
        {
        PORTD ^= ( 1 << PD7 );
        }


  }// Ende for

  return 0;

}// Ende main
[/c)

von batman (Gast)


Lesenswert?

Auf dem LCD erscheint die 10?

von Udo S. (Firma: allround) (1udo1)


Lesenswert?

Ja, jeder Übergang im Graycode erhöht den Wert "val" um einen Schritt. 
Dieser "val-Wert" wird im LCD auch angezeigt. Die Lichtschranken werden 
durch eine Encoderscheibe geschaltet. Also beide Impulse um 90 Grad 
versetzt.

Die Anzeige im LCD und am PORT C zeigt genau das an, was die 
Encoderscheibe an den 2 Lichtschranken bewirkt.

Vielleicht ist da irgendwo ein kapitaler Fehler, aber ich kann den nicht 
erkennen.

Habe das LCD auch in anderen Programmen getestet. Die Anzeigen stimmen 
mit den Werten in den Registern überein, also Addition, Subtraktion von 
Variablen. Alles o.k.

Bloß dieser abgefragte "val-Wert" in der If-Schleife lässt PD7 nicht 
toggeln.

von batman (Gast)


Lesenswert?

Der wird schon toggeln, nur wirst du es wohl nicht sehen. ;-)

von batman (Gast)


Lesenswert?

Ich meine, selbst wenn du PORTD auf Output schaltest, was in deiner main 
ja anscheinend nicht passiert.

von Udo S. (Firma: allround) (1udo1)


Lesenswert?

PD7 ist OUTPUT. Habe ich in der void set(void) deklariert.
1
   DDRD |=   0xf0;    // PD0, PD1, INT0 PD2 und INT1 PD3 Eingänge
2
   PORTD |=  0xff;    //  Pullup-activated, --- Activ LOW

... oder habe ich Dich falsch verstanden?

Ich checke das mal mit dem Scope. Vielleicht toggelt sie ja doch.Beim 
Togglen sollte die LED zumindest glimmen. Sie bleibt aber dunkel.

Werde mal in der If-Schleife ein
1
 PORTD |= 1<<PD7
 setzen. Vielleicht klappt das.

von Udo S. (Firma: allround) (1udo1)


Lesenswert?

Hallo,

ich habe die For-Schleife soweit minimiert und bitte um Hilfe von 
jemandem, der sich einen Reim auf folgende Situation machen kann.

PD7 wird nicht gesetzt.....

Ich möchte anhand der Werte von "val" einen Motor steuern. Aber ich kann 
den Wert von "val" nicht nutzen, um Positionen der Motorachse 
abzufragen.

Der komplette Code von Peter Dannegger ist oben schon gepostet. Der Code 
funktioniert, nur PD7 wird nicht gesetzt.
1
int main( void )
2
{
3
  uint16_t val = 0;
4
5
  LEDS_DDR = 0xFF;
6
  encode_init();
7
  sei();
8
9
  for(;;)
10
  {
11
    val += encode_read1();       // read a single step encoder
12
    LEDS = val;
13
    PORTD |= ( 1 << PD7 );   // wird nicht gesetzt
14
  }// Ende for
15
16
  return 0;
17
18
}// Ende main

von batman (Gast)


Lesenswert?

Puh, weißt du denn überhaupt, wie so ein Programm funktioniert?

Also nochmal, um den Ausgang zu aktivieren, muß der µC das folgende 
Kommando ausführen und dazu sollte es möglichst irgendwo am Anfang des 
Hauptprogramms stehen. Irgendwoanders "deklariert" hilft nicht.

DDRD |= 1<<PD7;

von Udo S. (Firma: allround) (1udo1)


Lesenswert?

Hallo batman,

DAAAAAAAAAAAAAAAAAAAAAAAAAAAANK an Dich!!!!!! Ich weiß, wie Programme 
funktionieren. Aber ich habe in der main vollkommen übersehen, daß ich 
set(); nicht aufgerufen habe. Dadurch waren die PORTs natürlich nicht 
deklariert. Das passiert, wenn man nicht so viel Routine hat.

Hatte den Kopf zu, wegen anderer Sachen. Du hast mir sehr geholfen. 
Nochmals Danke!!!

Udo

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.