Forum: Compiler & IDEs c code optimierung ev. assembler?


von Patrick B. (p51d)


Lesenswert?

Ich habe hier ein kleines Problem:

Die Ansteuerung 2-er DA-Wandler von einem MCU aus sollte sehr rasch 
erfolgen, leider wahrscheinlich zu rasch für normales c.

Zur Info:
control[] ist ein 8-Bit Array, bei dem die Bits 0 bis 4 für einen 
Delay-Wert verwendet werden und die Bits 5 bis 7 für LED-Zustände.
x_achse und y_achse sind je 16 Bit grosse Arrays, von denen eigenltich 
nur 12 Bits gebraucht werden.
SysCount ist eine Variable die von einem HW-Timer alle 10us 
inkrementiert wird. SysCount kann Werte von 3 bis 31 enthalten.

Die Funtkion die die Array-Werte an die Parallel-DAC ausgibt:
1
void DAC_Output(uint16_t val){    // converts 12Bit to the DA Converter
2
  if(val & (1<<8)){
3
    PORTC |= (1<<3);
4
  }
5
  else{
6
    PORTC &= (~(1<<3));
7
  }
8
  if(val & (1<<9)){
9
    PORTC |= (1<<2);
10
  }
11
  else{
12
    PORTC &= (~(1<<2));
13
  }
14
  if(val & (1<<10)){
15
    PORTC |= (1<<1);
16
  }
17
  else{
18
    PORTC &= (~(1<<1));
19
  }
20
  if(val & (1<<11)){
21
    PORTC |= (1<<0);
22
  }
23
  else{
24
    PORTC &= (~(1<<0));
25
  }
26
  PORTA = val;
27
}

und die Steuerung:
1
void Output(){
2
  static uint16_t p = 0;
3
  if((control[p] & (1<<7)) != 0){
4
    LED_red_on;
5
  }
6
  else{
7
    LED_red_off;
8
  }
9
  if((control[p] & (1<<6)) != 0){
10
    LED_green_on;
11
  }
12
  else{
13
    LED_green_off;
14
  }
15
  if((control[p] & (1<<5)) != 0){
16
    LED_blue_on;
17
  }
18
  else{
19
    LED_blue_off;
20
  }
21
  if((control[p] & 0x1F) == SysCount){
22
    DAC_Output(x_achse[p]);
23
    X_select;
24
    no_select;
25
    DAC_Output(y_achse[p]);
26
    Y_select;
27
    no_select;
28
    if((p == 299) || (p == 599)) RXTX_Reg = ENQ;
29
    if(p == 574) p = 0;
30
    else p ++;
31
    SysCount = 0;
32
  }  
33
}
wobei mir die Zeile "if((control[p] & 0x1F) == SysCount)" das ganze 
verunstalltet. Ergibt die Maske 3 so ist der SysCount zu schnell, nehme 
ich anstelle vom "==" ein ">" läufts etwas schneller, aber nicht viel.
Schreibe ich anstelle der Maske eine 3 so läuft das Programm im 
gewohnten Tempo (schnell).

Jetzt ist meine Frage, wie kann ich die Durchlaufszeit des Programms so 
minim wie möglich halten?
Die Funktion DAC_Output() in Assembler schreiben und der Teil mit den 
LEDs auch?
Einziges Problem: Ich habe in der Schule nur etwa 4 Lektionen Assembler 
gehabt, sonst nur C.

Was mir etwas Bedenken macht: Das ganze wird noch mit einem RS232-Teil 
ergänzt, welcher zwar schon im Prinzip "State-machine" aufgebaut ist, 
aber sicherlich noch etwas Rechenzeit in anspurch nimmt.

Habt ihr etwaige Vorschläge?
MFG
Patrick

von Stefan B. (Gast)


Lesenswert?

Je nach dem ob die Bits 8-11 in val gesetzt sind, setzt du die Bits 3-0 
in POSTC. Dazu verwendest du rel. umfangreiche if/else Konstruktinen.

Wie wäre es, die Bits iN PORTC geschickter zuzuordnen,damit die 
Reihenfolge identisch ist 8-11 => 0-3? Und dann über Bitmanipulation 
mit einer Bitmaske die 4 Bits auf einen Streich zu setzen? Ähnliches 
geht vielleicht auch beim Setzen der LEDs. Dann DAC_Output() ggf. noch 
als inline Funktion schreiben.

Insgesamt denke ich, dass du nicht "besonders" viel rausholst. Die 
Zeitfresser verstecken sich meiner Meinung nach woanders in deinem 
Programm.

von Patrick B. (p51d)


Lesenswert?

um die if/else-Geschichte durch einen einfachen Schiebebefehl wegmachen 
zu können, müsste ich Hardware-Änderungen vornehmen...(Leiterbahnen 
trennen und Drähtchen ziehen.

das Problem ist, dass das ganze Prog nicht länger als 20-30us (besser 
~10) Durchlaufszeit haben darf, und je nach dem, ob die hälfte der 
Arrays ausgegeben wurde, noch die RS232-Funktion und der RX-Interrupt 
dazukommt.
Zum Testen habe ich einfach mal in ner h Datei die Arrays 
hineingeschrieben und dann ausgegeben.

Was mich vorallem mistrauisch macht ist, dass es so extreme Unterschiede 
zwischen diesen beiden Varianten gibt:
1
if((control[p] & 0x1F) == SysCount)
2
if(3 == SysCount)
Der Ausmaskierte Wert würde auch 3 ergeben... Variante 1 hat Verzögerung 
und Variante 2 nicht.

Hier ist mal noch der Rest des Codes:
1
// Einbinden von bestehenden Modulen
2
#include <avr/io.h>                            // AVR Standart IO Register Definitionen
3
#include <stdio.h>                            // Standart IO Register
4
#include <avr/iom644.h>                          // ATMega644 Register Definitionen
5
#include <avr/iomxx4.h>                              // ATMegaXX4 Register Definitionen
6
#include <avr/interrupt.h>                        // Interrupt Register Definitionen
7
#include "arrays.h"
8
9
//------------------------------------------------------------------------------
10
// Definitionen
11
#define LED_green_on   (PORTD |= (1<<5))
12
#define LED_green_off (PORTD &= (~(1<<5)))
13
#define LED_red_on  (PORTB |= (1<<3))
14
#define LED_red_off  (PORTB &= (~(1<<3)))
15
#define LED_blue_on  (PORTD |= (1<<7))
16
#define LED_blue_off  (PORTD &= (~(1<<7)))     
17
#define X_select    (PORTB &= (~(1<<0)))
18
#define Y_select    (PORTB &= (~(1<<1)))
19
#define no_select    ((PORTB |= (1<<0)) & (PORTB |= (1<<1)))
20
21
#define search 0
22
#define waitSOH 1
23
#define waitCONTROL 2
24
#define waitAXIS 3
25
#define waitEOT 4
26
#define Buf_max 49
27
#define RXTX_Reg UDR0
28
#define ENQ 0x05
29
#define SOH 0x01                            // Start of Header
30
#define EOT 0x04                            // End of Transmission
31
32
#define F_CPU 24000000                          // Quarzfrequenz
33
#define BAUDRATE 3000000                        // Baudrate für Kommunikation
34
35
int uart_putchar(char c, FILE *stream);                  //privat für printf  
36
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL,_FDEV_SETUP_WRITE);
37
38
//------------------------------------------------------------------------------
39
// Variablen
40
static volatile int SysCount = 0;                    // Nutz-Counter
41
static volatile int index_rxBuf_R = 0;                  // Index wo zuletzt gelesen wurde
42
static volatile int index_rxBuf_W = 0;                  // Index wo zuletzt geschrieben wurde
43
static volatile int rxBuf[Buf_max+1];                  // RX Buffer
44
45
//int control[600]={};
46
//uint16_t x_achse[600]={};
47
//uint16_t y_achse[600]={};
48
49
//------------------------------------------------------------------------------
50
// Funktionen
51
void DAC_Output(uint16_t val){                      // converts 12Bit to the DA Converter
52
  if(val & (1<<8)){
53
    PORTC |= (1<<3);
54
  }
55
  else{
56
    PORTC &= (~(1<<3));
57
  }
58
  if(val & (1<<9)){
59
    PORTC |= (1<<2);
60
  }
61
  else{
62
    PORTC &= (~(1<<2));
63
  }
64
  if(val & (1<<10)){
65
    PORTC |= (1<<1);
66
  }
67
  else{
68
    PORTC &= (~(1<<1));
69
  }
70
  if(val & (1<<11)){
71
    PORTC |= (1<<0);
72
  }
73
  else{
74
    PORTC &= (~(1<<0));
75
  }
76
  PORTA = val;
77
}
78
void SysInit(){
79
  int i = 0; 
80
  
81
  DDRA = 0xFF;                            // IO Definieren
82
  DDRB = 0x1B;
83
  DDRC = 0x0F;
84
  PORTC = 0xF0;                            // Pull up einschalten
85
  DDRD = 0xF0;
86
  
87
  UBRR0H = ((F_CPU/(BAUDRATE*8L)-1) >> 8);                  // calc Baud
88
    UBRR0L = (uint8_t)(F_CPU/(BAUDRATE*8L)-1);               // Double speed
89
  UCSR0A |= (1<<U2X0);                        // RS232 Settings: double speed
90
  UCSR0B |= (1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0);            // Complete Interrupts, RX und TX einschalten
91
  UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);                  // Asynchron, 8-Bit  
92
  
93
  TCCR2A |= (1<<WGM21);                        // Timer2 löst mit 100kHz Interrupt aus
94
  TCCR2B |= (1<<CS21);
95
  TIMSK2 |= (1<<OCIE2A);
96
  OCR2A = 30;
97
98
  for(i=0;i<=Buf_max;i++){
99
    rxBuf[i] = 0;
100
  }
101
  DAC_Output(2048);
102
  X_select;
103
  Y_select;
104
  no_select;
105
  
106
  stdout = &mystdout;  
107
  
108
  sei();                                // Global Interrupts einschalten
109
}
110
111
int uart_putchar(char c, FILE *stream)      //Nur für Ausgabe mit printf
112
    {
113
      if (c == '\n')
114
        uart_putchar('\r', stream);
115
      loop_until_bit_is_set(UCSR0A, UDRE0);
116
      UDR0 = c;
117
      return 0;
118
    }
119
  
120
  
121
void RS232(void){
122
  static int RS232_state = 0;                      // Protokollstatus
123
  static int index_par = 0;                      // Array-Variable für Parameters
124
  static uint16_t k = 0;
125
  if(index_rxBuf_W == index_rxBuf_R){                  // Buffer Error abgleichen
126
    return;                              // Wenn Error => aus Funktion heraus springen
127
  }
128
  if(index_rxBuf_R == Buf_max){                    // Erhöhen
129
    index_rxBuf_R = 0;
130
  }
131
  else{
132
    index_rxBuf_R ++;
133
  }
134
  switch (RS232_state){                        // RS232 Protokoll
135
    case search:
136
      if(rxBuf[index_rxBuf_R] == '@'){
137
        RS232_state = waitSOH;
138
        printf("dac_board_v1");
139
      }
140
      break;
141
    case waitSOH:
142
      switch (rxBuf[index_rxBuf_R]){
143
        case SOH:  
144
          RS232_state = waitCONTROL;              // In nächsten State wechselnd
145
          break;
146
        case EOT:
147
          RS232_state = waitSOH;
148
          break;
149
        default:
150
          RXTX_Reg = NACK;
151
          break;
152
      }
153
      break;
154
    case waitCONTROL:
155
      control[k] = rxBuf[index_rxBuf_R];
156
      RS232_state = waitAXIS;
157
      break;
158
    case waitAXIS:
159
      switch (index_par){
160
        case 0:
161
          x_achse[k] = (rxBuf[index_rxBuf_R] << 4);
162
          index_par ++;
163
          break;
164
        case 1:
165
          x_achse[k] |= (x_achse[k - 1] | (rxBuf[index_rxBuf_R] >> 4));
166
          k ++;
167
          y_achse[k] = ((rxBuf[index_rxBuf_R] & 0x0F) << 8);
168
          index_par ++;
169
          break;
170
        case 2:
171
          y_achse[k] |= (y_achse[k - 1] | (rxBuf[index_rxBuf_R]));
172
          index_par = 0;
173
          RS232_state = waitEOT;
174
          break;
175
      }
176
      break;
177
    case waitEOT:
178
      if(rxBuf[index_rxBuf_R] == EOT){
179
        RS232_state = waitSOH;
180
      }
181
      break;
182
  }
183
  if(k == 599) k = 0;
184
  else k ++;
185
}
186
void Output(){
187
  static uint16_t p = 0;
188
  if((control[p] & (1<<7)) != 0){
189
    LED_red_on;
190
  }
191
  else{
192
    LED_red_off;
193
  }
194
  if((control[p] & (1<<6)) != 0){
195
    LED_green_on;
196
  }
197
  else{
198
    LASER_green_off;
199
  }
200
  if((control[p] & (1<<5)) != 0){
201
    LED_blue_on;
202
  }
203
  else{
204
    LED_blue_off;
205
  }
206
  if((control[p] & 0x1F) == SysCount){
207
    DAC_Output(x_achse[p]);
208
    X_select;
209
    no_select;
210
    DAC_Output(y_achse[p]);
211
    Y_select;
212
    no_select;
213
    if((p == 299) || (p == 599)) RXTX_Reg = ENQ;
214
    if(p == 574) p = 0;
215
    else p ++;
216
    SysCount = 0;
217
  }  
218
}
219
//------------------------------------------------------------------------------
220
// INTERRUPS
221
ISR(TIMER2_COMPA_vect){                          // Mit 100kHz SysCount erhöhen
222
  SysCount ++;
223
}
224
ISR(USART0_RX_vect){                          // Received Complete Interrupt
225
  if(index_rxBuf_W == Buf_max){                    // Damit Buffer nicht überläuft
226
    index_rxBuf_W = 0;
227
  }
228
  else{
229
    index_rxBuf_W ++;
230
  }
231
  if(index_rxBuf_W == index_rxBuf_R){                  // Wenn Write-Index Read-Index eingeholt hat
232
    return;
233
  }
234
  else{                                // Daten speichern
235
    rxBuf[index_rxBuf_W] = RXTX_Reg;
236
  }
237
}
238
//------------------------------------------------------------------------------
239
// Hauptfunktion (wird beim Start ausgeführt) 
240
int main(void){
241
    SysInit();                              // Systeminitialisierung
242
  while(1){
243
    RS232();
244
    Output();
245
  }
246
return 0;
247
}
248
//------------------------------------------------------------------------------

Der MCU ist wegen FTDI und Baudrate schon übertaktet: anstelle 20Mhz 
24Mhz.
Muss ich hier den MCU Wechseln und einen nehmen mit > 40Mhz oder was?

Danke für die rasche Antwort
MFG
P51D

von Stefan B. (Gast)


Lesenswert?

Ich sehe nicht wie dein control[] definiert ist, Ich vermute auch 
volatile? Siehst du Speed-Änderungen, wenn du das zu Beginn von Output() 
in eine lokale, non-volatile Variable kopierst und dann die Abfragen mit 
der lokalen Variablen machst? Das setzt allerdings voraus, dass sich 
control[] während der Laufzeit von Output() nicht ändern kann/soll.

Die Zuordnung der gedrehten Bits könnte man auch über eine 16-elementige 
Lookup-Tabelle versuchen, wenn der Platz im RAM das her gibt.

von Patrick B. (p51d)


Lesenswert?

die Arrays sind so definiert:
1
static int control[600]={};
2
static uint16_t x_achse[600]={};
3
static uint16_t y_achse[600]={}

Stefan B. schrieb:
> Die Zuordnung der gedrehten Bits könnte man auch über eine 16-elementige
> Lookup-Tabelle versuchen, wenn der Platz im RAM das her gibt.

durch die sehr grossen Arrays ist der minimale 4k SRAM schnell 
aufgebraucht.
erfolgreich getestet habe ich es bis jetzt aber nur mit 575 Werten 
grossen Arrays.

Das ganze kann man als FIFO ansehen, nur werden die UART-Werte noch 
etwas zerlegt.

also doch andere Controller?

von Stefan E. (sternst)


Lesenswert?

Patrick B. schrieb:
> Was mich vorallem mistrauisch macht ist, dass es so extreme Unterschiede
> zwischen diesen beiden Varianten gibt:
1
if((control[p] & 0x1F) == SysCount)
2
if(3 == SysCount)
> Der Ausmaskierte Wert würde auch 3 ergeben... Variante 1 hat Verzögerung
> und Variante 2 nicht.

Warum wundert dich das? Wenn control und p Variablen sind, muss das 
zur Laufzeit ausgerechnet werden. Das dauert natürlich länger wie der 
Vergleich mit einer simplen Konstante.

Du solltest mit den "Optimierungen" als erstes mal bei deinem 
Programmierstil beginnen. Warum sind z.B. alle Variablen 16 Bit groß, 
obwohl bei den meisten 8 Bit reichen würde?
Damit ist das hier auch wirklich kein Wunder:
> durch die sehr grossen Arrays ist der minimale 4k SRAM schnell
> aufgebraucht.
Ein großer Teil des verbrauchten Platzes ist einfach nur verschwendet. 
Und der Code wird dadurch auch deutlich verlangsamt.

von Patrick B. (p51d)


Lesenswert?

Stefan Ernst schrieb:
> Du solltest mit den "Optimierungen" als erstes mal bei deinem
> Programmierstil beginnen. Warum sind z.B. alle Variablen 16 Bit groß,
> obwohl bei den meisten 8 Bit reichen würde?
> Damit ist das hier auch wirklich kein Wunder:
Sorry, es ist schon etwas spät und vielleicht habe ich was vorhin beim 
überfliegen übersehen: wo hast du VIELE 16-Bit Variablen gefunden dies 
nicht sein müssten?
1. und 2. Variable:
> uint16_t x_achse[600]={};
> uint16_t y_achse[600]={};beide sind zur abspeicherung von 12-Bit Werten gedacht, 
hatte zwar mal vor, das mit Bitmuster zu lösen, aber da ist der Zugriff auch nicht 
der schnellste
3. Variable
> uint16_t val;
Dient zur übergabe von den Array-Werten (ok, könnte man weglassen, 
verunschöndert aber den Code
4. und 5. Variable
> static uint16_t k = 0;
> static uint16_t p = 0;
Dient zum Zugriff auf die 600-Plätze grossen Arrays. Kannst du mit einer 
8-Bit Variable bis auf 600 zählen? Ich nicht.

>> durch die sehr grossen Arrays ist der minimale 4k SRAM schnell
>> aufgebraucht.
> Ein großer Teil des verbrauchten Platzes ist einfach nur verschwendet.
> Und der Code wird dadurch auch deutlich verlangsamt.
?? Wie meinst du das?
Die Arrays machen den Grossteil des Platzes aus und die sind bewusst so 
gross, damit Windows, welches mir daten Schicken soll, später nicht alle 
30us Werte Verarbeiten muss, sondern zwischendurch auch mal ein paar 
sekunden Pause hat.

Bin immer offen für Kritik und Vorschläge, aber bitte nicht einfach so 
ins Grüne hinaus kritisieren.

MFG
P51D

von Stefan E. (sternst)


Lesenswert?

Patrick B. schrieb:
> Sorry, es ist schon etwas spät und vielleicht habe ich was vorhin beim
> überfliegen übersehen: wo hast du VIELE 16-Bit Variablen gefunden dies
> nicht sein müssten?

Gegenfrage: zeige mir eine einzige Variable, die nicht 16 Bit groß ist?
(Hinweis: "int" sind 16 Bit)

von Patrick B. (p51d)


Lesenswert?

Eine int Variable auf einem 8-Bit MCU ist nicht 16Bit gross.
Wenn das so sein würde, wäre ich in den letzten 2 Jahren vergebens zur 
Schule gegangen und hätte zahlreiche Projekte und Programme nicht 
speziell auf 16Bit int Variablen abändern müssen, weil die Berechnungen 
nicht mehr in einer normalen 8Bit Variable Platz gehabt hätten.

wozu dann auch noch uint16_t wenns doch schon 16 Bit gross wäre?
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Ganzzahlige_.28Integer.29_Datentypen

Sobalt die MCU grösse steigt steigt auch der Wertebereich eines Integer
8Bit MCU => 8Bit
16Bit MCU => 16Bit
32Bit MCU (oder PC Programmierung nix mit int32_t) => 32Bit

Also wenn du mir hier nicht zustimmst, dann habe ich wohl wirklich etwas 
falsch gemacht in den letzten 2 Jahren und ich entschuldige mich jetzt 
schon für meine Unwissenheit

von Karl (Gast)


Lesenswert?

Entschuldigung angenommen: int auf AVR-GCC sind 16 Bit! Doku lesen.

von Stefan E. (sternst)


Lesenswert?

Patrick B. schrieb:
> Eine int Variable auf einem 8-Bit MCU ist nicht 16Bit gross.
> Wenn das so sein würde, wäre ich in den letzten 2 Jahren vergebens zur
> Schule gegangen und hätte zahlreiche Projekte und Programme nicht
> speziell auf 16Bit int Variablen abändern müssen, weil die Berechnungen
> nicht mehr in einer normalen 8Bit Variable Platz gehabt hätten.

Ein int auf AVR ist 16 Bit groß. Warum? Weil der C-Standard 
vorschreibt, dass ein int mindestens 16 Bit haben muss.

von Patrick B. (p51d)


Lesenswert?

sorry, finde den Teil nicht wos etwas in der Art heisst wie "int steht 
für einen 16-Bit..."

finde nur das
>  int8_t steht für einen 8-Bit Integer mit einem Wertebereich -128 bis +127.

> uint8_t steht für einen 8-Bit Integer ohne Vorzeichen (unsigned int) mit
> einem Wertebereich von 0 bis 255

> int16_t steht für einen 16-Bit Integer mit einem Wertebereich -32768 bis
> +32767.

> uint16_t steht für einen 16-Bit Integer ohne Vorzeichen (unsigned int) mit
> einem Wertebereich von 0 bis 65535.

geht denn bei einer Initialisierung eines normalen Integer durch "int xy 
= 0;" der Wertebereich von -255 bis +255?

Habs mal rasch auf einem MCU-Board testen müssen, denn ist wirklich 
schwer so etwas zu glauben, wenn jemand einem das 4 Jahre lang anders 
beigebracht hat (hat zu Teil auch wirklich Auswirkungen bei Berechnungen 
gehabt, als hätte man nur einen 8-Bit Wert zur Verfügung). Tja man 
lehrnt nie aus.
Neue Erkenntniss über den Lehrer: er hat noch weniger Ahnung von nichts 
als ich bisher angenommen habe (zuteil auch belegt...) Scheiss 
Berufsschullehrer bringen einem nie etwas brauchbares bei...

OK, entschuldige mich nochmals in aller Form für meine "hartnäckigkeit". 
Dann werde ich die nichtbenötigten "int" in char umwandeln (diese sind 
doch aber 8 Bit? oder hat man uns da wieder was falsches beigebracht?)

MFG
P51D

von Stefan E. (sternst)


Lesenswert?

Patrick B. schrieb:

> geht denn bei einer Initialisierung eines normalen Integer durch "int xy
> = 0;" der Wertebereich von -255 bis +255?

Nein. "int" ist einfach das Gleiche wie int16_t.

> Dann werde ich die nichtbenötigten "int" in char umwandeln (diese sind
> doch aber 8 Bit? oder hat man uns da wieder was falsches beigebracht?)

Nimm auf keinen Fall "char" (außer es handelt sich tatsächlich um ein 
Zeichen). Nimm entweder "unsigned char" oder "signed char", oder noch 
besser uint8_t oder int8_t.

von Florian W. (Gast)


Lesenswert?

char sind 8 bit, so wurde es zumindest auch mir in Uni beigebracht.
Da war ich anfangs auch etwas verwundert warum ein Datentyp der 
scheinbar für Buchstaben gedacht ist auch gerne für kleine Zahlen 
verwendet wird.
Von VB (jaja, Schande über mich) was ich zu Schulzeiten nebenher 
programmiert hab ist man was anderes gewohnt.

Es gibt glaub ich irgendwo eine Option für den gcc, mit der int zu 8 bit 
wird, aber da das dem C-Standard widerspricht ist die Option inzwischen 
deprecated.

Sauberer ist meiner Meinung nach die Verwendung von uint8_t, uint16_t, 
etc., so hat man die Größe direkt bei der Deklaration im Blick.

von Karl (Gast)


Lesenswert?

Bei char ist AFAIR nicht definiert, ob es signed oder unsigned ist. 
Deshalb char für Zeichen, (u)int8_t für 8 Bit Zahlen. Einfach, oder? ;)

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.