ADC über SPI

OP #7342492
Lesenswert?

Hallo,

ich habe bereits viel gegoogelt, aber finde einfach keine Lösung für mein Problem...

Ziel: ADU Wert auslesen und an PC senden.

Hardware: Nucleo-Board mit STM32F446RE, AZDelivery UART-TTL USB Adapter mit CH340G und AD Wandler MCP3008

Problem: Über SPI lässt sich weder etwas an den ADU senden oder etwas auslesen. Das RX bzw. TX Buffer wird nach einem Lese- bzw. Schreibvorgang durch SPI1->DR nicht zurücksetzen.

Code:

1
#define SPI_Startbyte (0x1 << 7) 
2
#define SPI_SelectChannel   
3
#define SPI_RandomByte (0xFF)
4

5
// Funktionsprototypen
6
void sendeChar(char character);
7
void SPIEmpfange(uint8_t *data, int size);
8
void SPISende(uint8_t *data, int size);
9

10
int main(void)
11
{
12

13

14
  // Initialisierung der GPIO
15

16
  // Aktivieren der Clock für GPIO Port A
17
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
18

19
  // Definition von GPIO A Pin 5, 6, 7, 9 und 10 als alternative Funktion (Für USART1 und SPI1) und Pin 8 als Output (Für CS)
20
  GPIOA->MODER |= GPIO_MODER_MODER5_1; // SPI1 CLK
21
  GPIOA->MODER |= GPIO_MODER_MODER6_1; // SPI1 MISO
22
  GPIOA->MODER |= GPIO_MODER_MODER7_1; // SPI1 MOSI
23
  GPIOA->MODER |= GPIO_MODER_MODER9_1; // USART 1 TX
24
  GPIOA->MODER |= GPIO_MODER_MODER10_1; // USART 1 RX
25
  GPIOA->MODER |= GPIO_MODER_MODER8_0; // GPIO OUTPUT
26

27
  // Definition von GPIO A Pin 5, 6 und 7 als alternative Funktion AF5
28
  GPIOA->AFR[0] &= ~(GPIO_AFRL_AFSEL5);
29
  GPIOA->AFR[0] |= GPIO_AFRL_AFSEL5_0 | GPIO_AFRL_AFSEL5_2;
30

31
  GPIOA->AFR[0] &= ~(GPIO_AFRL_AFSEL6);
32
  GPIOA->AFR[0] |= GPIO_AFRL_AFSEL6_0 | GPIO_AFRL_AFSEL6_2;
33

34
  GPIOA->AFR[0] &= ~(GPIO_AFRL_AFSEL7);
35
  GPIOA->AFR[0] |= GPIO_AFRL_AFSEL7_0 | GPIO_AFRL_AFSEL7_2;
36

37
  GPIOA->AFR[0] |= (5<<20)|(5<<24)|(5<<28);
38

39
  // Definition von GPIO A Pin 9 und 10 als alternative Funktion AF7
40

41
  GPIOA->AFR[1] |= GPIO_AFRH_AFSEL9;
42
  GPIOA->AFR[1] &= ~GPIO_AFRH_AFSEL9_3;
43

44
  GPIOA->AFR[1] |= GPIO_AFRH_AFSEL10;
45
  GPIOA->AFR[1] &= ~GPIO_AFRH_AFSEL10_3;
46

47
  //Pegel an GPIO A Pin 8 auf High, damit SPI Modul ruht, bis die Kommunikation beginnt.
48
  GPIOA->BSRR |= GPIO_BSRR_BS8;
49

50

51
  // Initialisierung des SPI
52
        
53
        // Clock für die SPI 1 Schnittstelle
54
  RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
55

56
  // SPI Clock Polarität (CPOL) und Phasenverschiebung (CPHA) werden eingestellt
57
  SPI1->CR1 &= ~SPI_CR1_CPHA_Msk;
58
  SPI1->CR1 &= ~SPI_CR1_CPOL_Msk;
59

60
  // Selektieren, ob es sich um einen Master oder Slave handelt
61
  SPI1->CR1 |= SPI_CR1_MSTR;
62

63
  // Einstellen der Baudrate = Clock des jeweiligen SPI Bus (fPCLK) / Prescaler (PSC)
64
  SPI1->CR1 |= SPI_CR1_BR; // 111: fPCLK/256
65

66
  // SPI1 wird aktiviert
67
  SPI1->CR1 |= SPI_CR1_SPE;
68

69
  // Format of the data: LSB
70
  SPI1->CR1 |= SPI_CR1_LSBFIRST;
71

72
  // Software Slavemanagement: 
73
  // SSM = SSI = 1 --> Software Slave management
74
  SPI1->CR1 |= SPI_CR1_SSM;
75
  SPI1->CR1 |= SPI_CR1_SSI;
76

77
  // SPI1->CR1 Bit 15 = 0: 2 Leitungen --> Voll-Duplex
78
  SPI1->CR1 &= ~(SPI_CR1_BIDIMODE_Msk);
79
  SPI1->CR1 &= ~(SPI_CR1_RXONLY_Msk);
80

81
  // DFF: Data frame format: 8-Bit
82
  SPI1->CR1 &= ~(SPI_CR1_DFF_Msk);
83

84
  // Das Register CR2 wird erstmal nicht genutzt
85
  SPI1->CR2 = 0;
86

87
  uint8_t initData[3] = {(uint8_t)SPI_Startbyte, (uint8_t)SPI_SelectChannel, (uint8_t)SPI_RandomByte};
88

89
  //Pegel an GPIO A Pin 8 auf Low, damit SPI Modul für die Kommunikation aktiv ist.
90
  GPIOA->BSRR |= GPIO_BSRR_BR8;
91

92
  // Senden der Startkonfig an SPI:
93
  SPISende(initData,3);
94

95
  //Pegel an GPIO A Pin 8 auf High, damit SPI Modul ruht, bis die Kommunikation beginnt.
96
  //GPIOA->BSRR |= GPIO_BSRR_BS8;
97

98

99
       // Initialisierung USART1
100

101
       // Aktivieren der Clock für USART 1 Schnittstelle
102
  RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
103

104
  // Aktiviere/Starte USART1
105
  USART1->CR1 |= USART_CR1_UE;
106

107
  // 1 Start-Bit and 8 Databits and n Stopp-Bits
108
  USART1->CR1 &= ~USART_CR1_M;
109

110
  // Oversampling 16
111
        USART1->CR1 &= ~USART_CR1_OVER8_Msk;
112

113
  // Baudrate einstellen:
114
  USART1->BRR = 0x8B;
115

116
  // Aktiviere/Starte Sender
117
  USART1->CR1 |= USART_CR1_TE;
118

119
        uint8_t messDaten = 0;
120

121
  // Einfacher Test
122

123
  while(1)
124
  {
125
    GPIOA->BSRR |= GPIO_BSRR_BR8;
126
    SPIEmpfange(&messDaten, 1);
127
    GPIOA->BSRR |= GPIO_BSRR_BS8;
128
    sendeChar(messDaten);
129
    sendeChar('5');
130
  }
131
}
132

133
void SPISende(uint8_t *data, int size)
134
{
135
  int i = 0;
136
  while (i < size)
137
  {
138
    while (!((SPI1->SR) & (SPI_SR_TXE))){};
139

140
    SPI1->DR = data[i];
141

142
    i++;
143
  }
144

145
  while (!(SPI1->SR & SPI_SR_TXE)){};
146

147
  while(SPI1->SR & SPI_SR_BSY){}; 
148
  uint8_t temp;
149
  temp = SPI1->DR;
150
  temp = SPI1->SR; 
151
}
152

153
void SPIEmpfange(uint8_t *data, int size)
154
{
155
  while (size)
156
  {
157
    while(SPI1->SR & SPI_SR_BSY){};
158

159
    SPI1->DR = 0; // senden von dummy Daten
160

161
    while (!(SPI1->SR & SPI_SR_RXNE)){}; 
162

163
    *data++ = (SPI1->DR); 
164
    size--;
165
  }
166
}
167

168
void sendeChar(char character)
169
{
170
  USART1->DR = character;
171
  while(!(USART1->SR & USART_SR_TC)){;}
172
}
#7342507
Lesenswert?

Bei SPI existieren verschiefene Modi:

https://de.m.wikipedia.org/wiki/Serial_Peripheral_Interface#:~:text=SLK%20(englisch%20Serial%20Clock)%20auch,englisch%20Master%20Output%2C%20Slave%20Input

Siehe Kapitel Protokollablauf und Einstellmöglichkeiten

Vielleicht ist im STM ein anderer Mode (Parameter „Clock Polarity“ (CPOL) und „Clock Phase“ (CPHA)) als beim ADC eingestellt.

Gast #7342517
Lesenswert?

Gerald K. schrieb:

Bei SPI existieren verschiefene Modi:

https://de.m.wikipedia.org/wiki/Serial_Peripheral_Interface#:~:text=SLK%20(englisch%20Serial%20Clock)%20auch,englisch%20Master%20Output%2C%20Slave%20Input

Vielleicht ist im STM ein anderer Mode (Parameter „Clock Polarity“ (CPOL) und „Clock Phase“ (CPHA)) als beim ADC eingestellt.

Ja. Das ist ein Problem. Ein ganz anderes (und allein auf STM-Mist gewachsenes) ist die Unterstützung für einen doch eher exotischen MultiMaster-SPI-Bus und die an völligen Schwachsinn grenzende Idee, dass auch noch zum Default für den SPI zu machen.

Die Typen sind doch völlig gehirnamputiert!!!

Allerdings: ob das auch bei dem genannten konkreten STM32 so verhält, entzieht sich derzeit meiner Kenntnis. Das möge der TO selber im passenden DB nachschlagen.

Gast #7342529
Lesenswert?

RT schrieb:

SPISende(initData,3);

Das nützt erst mal gar nichts da der Wandler aktuell bei jedem Lesen konfiguriert wird.

RT schrieb:

void SPIEmpfange(uint8_t *data, int size)

{ ...... }

Das ist Käse. Lies das Datenblatt auf Seite 20, dort steht was zu tun ist: ein Konfigurations-Byte schreiben und danach zwei Bytes abholen. Tip: für jedes abzuholende Byte wird auch erst eines geschrieben.

RT schrieb:

#define SPI_SelectChannel

Und was soll dieses define bitte sehr?

Gast #7342644
Lesenswert?

Bei meinem STM32H7 benutze ich: HAL_SPI_TransmitReceive() Das wurde von STM mitgeliefert, und funktionierte auf Anhieb. Gibt es so etwas nicht für den STM32F, dass man alles von Hand machen muss?

Als nächstes würde ich ein Scope anschliessen, um zu sehen, was auf dem Bus los ist.

#7342897
Lesenswert?

Anbei meine C-Funktion um die MCP3008 bzw. MCP3208 via SPI auszulesen. Die MCPs verstehen SPI-Mode 0 und 1. Die sind eigentlich mega simpel zu verwenden und die Datenblätter sind IMHO gut beschrieben.

1
uint16_t getValueFromMCP(uint8_t channel, uint8_t mode){
2
  uint8_t message[3] = {0b100|(mode << 1)|((channel & 0b100) >> 2), (channel & 0b11) << 6, 0x00};
3
  uint8_t answer[3] = {0x00, 0x00, 0x00};
4
  spiTransferMessage(message, answer, sizeof(message)/sizeof(uint8_t), &mcp3208);
5
  return (uint16_t)(((answer[1] & 0x0f) << 8)|answer[2]);
6
}

Sollte (zusammen mit dem Datasheet des MCP) selbsterklärend sein. Eigentlich mega einfach.

spiTransferMessage erwartet zwei Arrays (ginge eigentlich auch eines, die Größe des Arrays und ein SPI-Device (sodass man weiß, welcher Pin der CS-Pin für das Device ist)

OP #7343589
Lesenswert?

Vielleicht ist im STM ein anderer Mode (Parameter „Clock Polarity“ (CPOL) und „Clock Phase“ (CPHA)) als beim ADC eingestellt.

Die Modi kenne ich. Ich habe auch geschaut (Datenblatt und diverse Implementierungen für Raspberry Pi) und meine da die richtige Einstellung zu haben.

Aktuell habe ich glaube ich den falschen Pegelwandler, weshalb ich auf CS kein High Signal bekomme, dass der ADU erkennt. Ich hab mir die "richtige" Hardware bestellt und probiere es nochmal aus.

OP #7343592
Lesenswert?

fuck schrieb:

RT schrieb:

SPISende(initData,3);

Tip: für jedes abzuholende Byte wird auch erst

eines geschrieben.

RT schrieb:

#define SPI_SelectChannel

Und was soll dieses define bitte sehr?

Vergiss das Define.

Ich hatte gedacht man schickt die 3 Byte aus dem Datenblatt und darauf folgen dann nur noch Bytes die erhalten werden ... Dann habe ich das falsch verstanden.

Danke

OP #7345430
Lesenswert?

M. K. schrieb:

Anbei meine C-Funktion um die MCP3008 bzw. MCP3208 via SPI auszulesen. Die MCPs verstehen SPI-Mode 0 und 1. Die sind eigentlich mega simpel zu verwenden und die Datenblätter sind IMHO gut beschrieben.

1
uint16_t getValueFromMCP(uint8_t channel, uint8_t mode){
2
>   uint8_t message[3] = {0b100|(mode << 1)|((channel & 0b100) >> 2), 
3
> (channel & 0b11) << 6, 0x00};
4
>   uint8_t answer[3] = {0x00, 0x00, 0x00};
5
>   spiTransferMessage(message, answer, sizeof(message)/sizeof(uint8_t), 
6
> &mcp3208);
7
>   return (uint16_t)(((answer[1] & 0x0f) << 8)|answer[2]);
8
> }

Also das verstehe ich nicht.

Ich möchte den ADU im single-ended Mode und Channel 0 nutzen.

gemäß deinem Code wären die messages: uint8_t message[3] = {0b100|(1 << 1)|((0 & 0b100) >> 2), (0 & 0b11) << 6, 0x00}; uint8_t message[3] = {0b110, 0b00000000, 0x00};

ich hätte aber gedacht man müsste der Reihenfolge entsprechend: uint8_t message[3] = {0b00000001, 0b10000000, 0x00};

Ich habe die Auslese-Funktion geändert in

1
void SPICommunicate(uint8_t *transmittedData, uint8_t *receivedData, int size)
2
{
3

4
  int i = 0;
5
    //Pegel an   uint8_t transmittedData[3] = {(uint8_t)SPI_Startbyte, (uint8_t)SPI_SelectChannel, SPI_RandomByte};
6
    // GPIO A Pin 8 auf Low, damit SPI Modul für die Kommunikation aktiv ist.
7

8
    GPIOB->BSRR |= GPIO_BSRR_BR0;
9
  while (i < size)
10
  {
11
    while (!((SPI1->SR) & (SPI_SR_TXE))){};
12
    SPI1->DR = transmittedData[i];
13

14
    receivedData[i] = SPI1->DR;
15

16
    i++;
17

18
    // Warten, bis der RX Zwischenspeicher leer ist
19
    while (!(SPI1->SR & SPI_SR_RXNE)){};
20

21
    // Am Anfang der Nachricht wird nochmal zur Sicherheit gewartet bis das SPI Protokoll nicht mehr beschäftigt ist!
22
    while(SPI1->SR & SPI_SR_BSY){};
23
  }
24

25
    //Pegel an GPIO B Pin 0 auf High, damit SPI Modul ruht, bis die Kommunikation beginnt.
26
    GPIOB->BSRR |= GPIO_BSRR_BS0;
27
}

Es wird kein Byte gesendet, glaube ich, weil die (SPI1->SR) & (SPI_SR_TXE) Register nicht zurückgesetzt werden.

Gast #7345465
Lesenswert?

RT schrieb:

Es wird kein Byte gesendet, glaube ich

Glauben kann man viel. Ich kanns nicht mit ansehen ..... Bring doch mal Ordnung in dein Chaos.

Hier mein Vorschlag das Ganze übersichtlich zu gestalten:

1
uint8_t SPI_TransferByte (uint8_t val)
2
{
3
  uint8_t ret_val;
4

5
  /*  wait for TX empty  */
6
  while ((SPI1->SR & SPI_I2S_FLAG_TXE) == 0)
7
  { /*  wait  */  };
8
  SPI1->DR = (uint8_t)(val);
9

10
  /*  wait for RX not empty  */
11
  while ((SPI1->SR & SPI_I2S_FLAG_RXNE) == 0)
12
  { /*  wait  */  };
13
  ret_val = SPI1->DR;
14

15
  /*  wait for Busy flag not active  */
16
  while ((SPI1->SR & SPI_I2S_FLAG_BSY) != 0);
17

18
  return ret_val;
19

20
} /* --- SPI_TransferByte --- */
21

22
uint16_t Read_ADC (void)
23
{
24
  uint8_t   byte_low, byte_high;
25
  uint8_t   config_byte;
26
  uint16_t  adc_val;
27
  uint8_t   mode = 1;
28
  uint8_t   channel = 0;
29

30
  config_byte = 0b100|(mode << 1) | ((channel & 0b100) >> 2);
31

32
  GPIOB->BSRR |= GPIO_BSRR_BR0;      // CS aktiv
33
  SPI_TransferByte (config_byte);    // Konfiguration schreiben
34
  byte_high = SPI_TransferByte (0);  // dummy schreiben, byte holen
35
  byte_low = SPI_TransferByte (0);   // dummy schreiben, byte holen
36
  GPIOB->BSRR |= GPIO_BSRR_BS0;      // CS inaktiv
37

38
  adc_val = ( ((uint16_t)byte_high) << 8) || ( (uint16_t)byte_low);
39

40
  return  adc_val;
41

42
} /* --- Read_ADC --- */

<SPI_TransferByte > ist voll ausgetestet, bei <Read_ADC> musst du noch geeignete Werte für mode und channel einsetzen. Vielleicht sind da noch noch kleine Unsauberkeiten drin, aber so kann man das Ganze wenigstens sauber überblicken.

Gast #7345492
Lesenswert?

Peter D. schrieb:

Warum will man aber einen externen 10Bit-ADC benutzen, wenn der STM32F446RE schon 3*12Bit-ADCs mit 24 Eingängen intern hat?

Weil sich der nur nutzen lässt, wenn der Pin nicht bereits mit einer anderen alternativen Funktion benutzt wird - was bei kleineren Pin-Counts schon mal vorkommt. Abgesehen davon sind die fertigen Entwicklungsboards selten mit einer hochstabilen und sauberen Referenzspannung versehen. Die Wandler werden zu gern mit der Versorgungsspannung verbunden. Die Dinger bekommt man nie ruhig. Am Ende würde ich die 3.3V sehen, die im Verhältnis zur unsauberen Referenzspannung sehr gering ausfällt. Ich nutzte auch einen diskreten 12Bit AD-Wandler, der über SPI an einen STM32F746 angeheftet wurde. Der lässt sich gut und stabil betreiben.

Ich habe mit bei der Programmierung des SPI an einem vorhandenen Init für ein Touch-Panel orientiert. Beim DMA muss man aber unbedingt auf die ERATA zum STM Controller achten. Die STM32 sind sehr interessante Cortex-M Vertreter, aber sie haben manchmal sehr üble Bugs in den ersten Versionen. DMA geht manchmal nicht wie beschrieben, wenn andere Ressourcen zeitgleich verwendet werden. Deren Interrupt-Handling ist auch gern mal problematisch. Also - sollte ein Verhalten nicht erklärbar sein, nach einem Erata schauen.

#7345514
Lesenswert?

RT schrieb:

gemäß deinem Code wären die messages: uint8_t message[3] = {0b100|(1 << 1)|((0 & 0b100) >> 2), (0 & 0b11) << 6, 0x00}; uint8_t message[3] = {0b110, 0b00000000, 0x00};

Schau dir noch mal genau an, was mein Code macht. Für Kanal 0 im Single-Ended Mode hast du die Bytes richtig ausgerechnet aber direkt danach schreibst du:

RT schrieb:

ich hätte aber gedacht man müsste der Reihenfolge entsprechend: uint8_t message[3] = {0b00000001, 0b10000000, 0x00};

und das ist natürlich nicht das selbe wie:

RT schrieb:

uint8_t message[3] = {0b110, 0b00000000, 0x00};

Dass er bei dir dann aber auch gar kein Byte sendet ist direkt noch ein anderes/weiteres Problem. Hast du denn zumindest ein Clock-Signal? Wenn du das nicht hast klingt das für mich schwer danach, dass dein SPI-Transmitter/Master gar nicht erst arbeitet, dann musst du da noch mal ran.

OP #7422440
Lesenswert?

Hallo,

ich hatte jetzt lange keine Zeit mich mit dem Thema zu beschäftigen und sitzte wieder daran.

Ich verstehe das SPI Prinzip irgendwie nicht.

Also wenn ein Bit gesendet wird, erhält man stets ein Bit zurück.

Es gibt bei der initialisierung des SPI für STM32FXX Prozessoren das Register SPI control register 1 (SPI_CR1).

Und dort gibt es die Bits

Bit 10 (RXONLY): Im Fall: Bit 10 = 0 --> Das SPI-Modul kann nur Nachrichten empfangen, Im Fall Bit 10 = 1: Das SPI-Modul kann Nachrichten empfangen und senden? Bit 14 (BIDOE): Aktiviert oder deaktiviert den Ausgang? Bit 15 (BIMODE): Entscheidet, ob die Verbindung voll Duplex ist oder halbduplex?

Wenn ich jetzt nur Messwerte eines ADU auslesen möchte, welchen Wert müssen die oben genannten Bits haben, kann mir das einer erklären?

schöne Grüße

#7422446
Lesenswert?

RT schrieb:

Bit 10 (RXONLY): Im Fall: Bit 10 = 0 --> Das SPI-Modul kann nur Nachrichten empfangen, Im Fall Bit 10 = 1: Das SPI-Modul kann Nachrichten empfangen und senden? Bit 14 (BIDOE): Aktiviert oder deaktiviert den Ausgang? Bit 15 (BIMODE): Entscheidet, ob die Verbindung voll Duplex ist oder halbduplex?

Wenn ich jetzt nur Messwerte eines ADU auslesen möchte, welchen Wert müssen die oben genannten Bits haben, kann mir das einer erklären?

Sollte im DB stehen. Man muß halt bloß auch den Rest lesen und nicht nur die Bezeichner der Bits...

Fazit: Faulschwein. Aber sowas von...

Beitrag #7422464 wurde von einem Moderator gelöscht.
OP #7424791
Lesenswert?

Okey jetzt habe ich den Fehler gefunden. Ich habe die SPI Einstellungen auf Default gesetzt, die SPI Frequenz mit dem BR Teiler so stark reduziert, wie im Datenblatt gefordert und Das SPI nach den Einstellungen aktiviert. Jetzt klappt beinahe alles wie gewollt.

Das einzige, was nicht funktioniert ist, ich kriege falsche Werte vom ADU.

Ich habe ein VDD = VREF = 3,3V

Somit sollte, wenn am Eingang des ADU 3,3V vorliegt den 16-Bit Wert 65.536 erhalten. Aber ich erhalte nur ca. 50.000.

Ich frage mich woran das liegt.

Ich habe den Code angehangen. Weiß einer weiter?

Angehängte Dateien:
#7424812
Lesenswert?

Das ist doch ein 10-bit AD-Wandler. Der kann nur 0...1023 ausgeben. Im Datenblatt FIGURE 6-1: SPI Communication with the MCP3004/3008 using 8-bit segments (Mode 0,0: SCLK idles low).

steht ganz explizit vorgegeben wie die Kommunikation aussehen soll. Sogar mit Beispiel. Deine SW macht m.E. was anderes.

OP #7424826
Lesenswert?

Nils S. schrieb:

Das ist doch ein 10-bit AD-Wandler. Der kann nur 0...1023 ausgeben. Im Datenblatt FIGURE 6-1: SPI Communication with the MCP3004/3008 using 8-bit segments (Mode 0,0: SCLK idles low).

steht ganz explizit vorgegeben wie die Kommunikation aussehen soll. Sogar mit Beispiel. Deine SW macht m.E. was anderes.

Hallo,

ich habe mir das angesehen und ja du hast recht mit den 10-Bit. 3,3 V wären dann 1024. Ich habe den Ausleseteil derartig geändert, dass die Bits 9 bis 0 aus der 16-Bit Variable verwendet werden (Mit der Zeile *WertDesADUx &= 0x3FF).

Ich habe jetzt einen kleinen Offset. Jetzt erhalte ich bei 3,3V Eingang ca. die Dezimalzahl 950, obwohl es 1024 sein sollten. Kann sich jemand die Differenz erklären?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren