Forum: Projekte & Code 16 Bit Soft-PWM auf (fast) beliebig vielen AVR-Ports


von Thomas A. (thomasa)


Lesenswert?

=== 16Bit SoftPWM 'Step by Step' (exemplarisch mit 
AVR)=====braincode@gmx.de 09/2009 Thomas AHlendorf

Hier mal ein typischer Fall von etwas worüber man monatelang nachdenkt 
und wenn man dann aufgibt (oder keine Zeit hat) dann fällts einem 
einfach ein ...

>Ein Hinweis für  den Leser:
< Die unten genannten Ideen sind auf meinem Mist gewachsen, ich stelle 
es aber jedem frei sie zu benutzen und für sich zu modifizieren
< Sollte ein unten eingebrachter Gedanke nutzbringend in ein Grösseres 
oder komerzielles Projekt einfliessen wäre die
< Erwähnung des Urhebers und dessen Benachrichtigung ein netter Zug.
< Sollte das Geschriebene unten hahnebüchen und unnütz sein will ichs 
gar nicht wissen...
< Falls jemand die gleichen Ideen gerade 5min vorher veröffentlicht hat 
soll es mir eine Ehre sein in Seinen Fusstapfen zu stolpern...:-}
>Ende des Hinweises

>noch ein Hinweis für  den Leser:
<Für Rechtschreibfehler und grammatikalische Ausrutscher bin ich leider 
auch verantwortlich und bitte um nachsicht
<(hätt ich doch WORD genommen,hab ich aber nicht!)
<Der Code ist nicht vollständig und evl. sind Variablen nicht deklariert 
(schande). Der Text ist mehr ein Tutorial und als Anregung gedacht, soll 
aber wohl hierhin gehören.
>Ende des 2.Hinweises

// Erst mal 8Bit SoftPWM
//#####################################################

Pulsweitenmodulation an sich ist ja eigentlich nicht weiter dramatisch, 
ich setz die Grundzüge einfach mal vorraus.
Die technische Umsetzung bei nutzung der Hardware z.B. des AVR ist auch 
machbar - dann aber kommen die Einschränkungen:

- Hardware PWM ist super, aber es gibt leider nicht genügend kanäle (für 
einige Anwendungen / beim AVR)
- Also Sofware-PWM; geht ganz gut - aber wenn man einfach die HW-PWM 
sofwaremässig abbildet geht mit
  hängen und biegen nur 8 Bit bevor z.B. LEDs flimmern.
- Glühlampen und Motoren sind kein Problem; aber LEDs die mit 8Bit 
Auflösung gedimmt werden schnippen
  dann   einfach beim übergang von 0 zu 1 (von 255) an, weil man sie 
einfach schon sieht wenn sie
  ~30us bei 100Hz an sind.
- 256 Stufen sind einfach zu wenig, zumal LEDs eigentlich noch 
expotentiell gedimmt werden müssten um einen
  gleichmässigen Helligkeitsanstieg zu ermöglichen.
- Also höhere Auflösung - wo wir wieder beim anfang der Schwierigkeiten 
wären

Der erste Ansatz zur Lösung war ein Artikel in einem Forum (weiss leider 
nicht mehr wo) in dem vorgeschlagen wurde die PWM-Werte vorher zu 
berechnen, in

einem Array abzulegen (LookUp) und dann im IRQ schnell auszugeben.
(immer gleich 8 Kanäle --> ein Port)

..braucht schon bei 256 Stufen ziemlich viel Speicher, aber immerhin.

Es ist ja eigentlich auch ineffektiv die Werte jedesmal neuzuberechnen 
auch wenn sie sich nicht ändern und noch dazu
in der Zeitkritischen IRQ Routine. Also mal losgelegt:

//#####################################################
1
uint8_t Kanal[8];  //Kanalwert 0..255
2
uint8_t pwm_arr[255];    //0=alles aus, 1 ist nur das Bit in arr[0] an (je nach Kanal), bei 255 alle an
3
4
ISR(TIMER0_COMPA_vect) //~30us *255 ~ 8ms ~ 125Hz
5
{
6
 static uint_8 pwm=0;  //lokal and statisch
7
 PORTD=pwm_arr[pwm++];  //
8
 if (pwm>=254) pwm=0; //!!
9
}
10
11
void calc_pwm()
12
{
13
 uint8_t pwmpos;
14
 
15
 for (pwmpos=0; pwmpos<255; ++pwmpos)
16
 {
17
  uint8_t val=0,chn;
18
  for (chn=0; chn<8; ++chn)
19
  {
20
   if (Kanal[chn]>pwmpos)
21
    val|=1<<chn; 
22
  }
23
  pwm_arr[pwmpos]=val;
24
 }
25
}
26
27
int main()
28
{
29
 for(;;)
30
 {
31
  //..mach irgendwas
32
  calc_pwm();    
33
 }
34
}
Das pwm_arr uebrigens sieht so aus:

         1.Byte       2.Byte      3.Byte   | |   255.Byte
         index 0     index 1     index 2   \  \  index 254
Kanal0 [1.255stel] [2.255stel] [3.255stel] [\  \54.255stel]
Kanal1 [1.255stel] [2.255stel] [3.255stel] [|  |54.255stel]
Kanal2 [1.255stel] [2.255stel] [3.255stel] [|  |54.255stel]
Kanal3 [1.255stel] [2.255stel] [3.255stel] [|  |54.255stel]
Kanal4 [1.255stel] [2.255stel] [3.255stel] /  /254.255stel]
Kanal5 [1.255stel] [2.255stel] [3.255stel]/  /[254.255stel]
Kanal6 [1.255stel] [2.255stel] [3.255stel]|  |[254.255stel]
Kanal7 [1.255stel] [2.255stel] [3.255stel]|  |[255.255stel]

//#####################################################

Kleine Anmerkung: man kann die PWM-Frequenz einfach erhöhen indem man 
die angeschalteten Bits mehr oder minder regelmässig über das Array 
verteilt.

Dabei Tauchen Begriffe wie Puls-Dichte-Modulation,Delta-Sigma bzw 
1Bit-DA wandlung auf. Aus Gründen die später noch offensichtlich werden 
nutze ich einfach

ein vorberechnetes Feld mit verteilten Werten. Welche und ob überhautpt 
eine der o.g. Methoden darstellt weis ich gar nicht. Ich lass es erst 
mal aus dem Code raus damit es übersichtlicher bleibt.
1
//Hier als Beispiel für 8Bit:
2
const uint8_t pwmmask[] PROGMEM = {
3
 0x0, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
4
 0x8, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
5
 0x4, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
6
 0xc, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
7
 0x2, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
8
 0xa, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
9
 0x6, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
10
 0xe, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
11
 0x1, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
12
 0x9, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
13
 0x5, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
14
 0xd, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
15
 0x3, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
16
 0xb, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
17
 0x7, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
18
 0xf, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
19
};
20
21
//Hier als Beispiel für 4Bit: //!! ;-)
22
const u8 sidemask16[] PROGMEM = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf};

//#####################################################

Soweit so gut, aber 8Bit bringen's leider nicht. Man müsste, dachte ich 
zuerst, einen der 255 PWM Positionen herausnehmen
(oder einen 256sten dazu) und die Bits nur mal kurz anschalten um eine 
höhere auflösung zu bekommen.

Aber dann fiel mir ein das der IRQ ja sowieso 256 mal pro Zyklus 
aufgerufen wird und wenn man bei jedem
Aufruf die Pins für einen Zeitraum der den 256sten Teil der IRQ-Zeit 
vorschaltet und dann die restliche Zeit den normalen
PWM-Wert dann hat man quasi schon mal 16 Bit Auflösung!
1
//prozessor läuft mit 8MHz -> 1Zyklus=125ns
2
3
//16bit Kanalwerte
4
uint16_t Kanal[8];  //Kanalwert 0..6535
5
6
//vorberechnetes Feld für die Oberen 8Bit des Kanalwertes
7
uint8_t pwm_arrhi[255];    //0=alles aus, 1 ist nur das Bit in arr[0] an (je nach Kanal), bei 255 alle an
8
9
//vorberechnetes Feld für die unteren 8Bit  des Kanalwertes
10
uint8_t pwm_arrlo[255];    //0=alles aus, 1 ist nur das Bit in arr[0] an (je nach Kanal), bei 255 alle an
11
12
13
//sollte alle 31,87us sein , geht aber auch 32
14
ISR(TIMER0_COMPA_vect) //~30us *255 ~ 8ms ~ 125Hz
15
{
16
 static uint_8 pwm=0;  //lokal and statisch
17
18
 uint8_t valhi=pwm_arrhi[pwm];//schon mal holen damit das ausgeben nachher nur einen Takt benötigt!!
19
20
 PORTD=pwm_arrlo[pwm++];  //maske für die unteren 8 Bit ausgeben
21
 
22
 PORTD=valhi;      //nach einem Takt (125ns@8MHz) den wert bis zum neuen IRQ
23
24
 if (pwm>=254) pwm=0; //!!
25
}
26
27
void calc_pwm()
28
{
29
 uint8_t pwmpos;
30
 
31
 for (pwmpos=0; pwmpos<255; ++pwmpos)
32
 {
33
  uint8_t vallo=0,valhi=0,chn;
34
35
  for (chn=0; chn<8; ++chn)
36
  {
37
   if ((Kanal[chn]&255)>pwmpos)  //schön getrennt nach Bytes
38
    vallo|=1<<chn; 
39
   if ((Kanal[chn]>>8)>pwmpos)
40
    valhi|=1<<chn; 
41
  }
42
43
  pwm_arrlo[pwmpos]=vallo;
44
  pwm_arrhi[pwmpos]=valhi;
45
46
 }
47
}

Ich hab die 16Bit des Kanalwertes in 2 x 8Bit Werte aufgeteilt die je 
255 bytes (bei 8 Kanälen) also insgesamt
510 Bytes einnehmen!!.

Schon mal ein kleines VOILA für 16BitPWM, aber der Speicherbedarf ist 
enorm und im Grunde sind (mir) die IRQs noch viel zu häufig und der 
Speicher zu schade.

//#####################################################

Nun sind das ja alles nur Codefragmente die zwar 16Bit PWM beschreiben 
aber immer nur mit 8 Kanälen =1Port!
Wenn man auf allen Ports eines AVR PWM ausgeben möchte (für zB eine 
Handvoll RGB-LEDs) wird das Speicherproblem bald erdrückend.


//#####################################################

Nächster Ansatz:

Wenn man anstelle 2 Gruppen zu je 8Bit einfach 4 Gruppen zu 4Bit nehmen 
würde käme man auf 4x15 - also 60 Bytes.
klingt schon moderater; die Berechnung verkompliziert sich etwas und der 
IRQ wird anders gestaltet:

Der 16 Bit Kanalwert wird in 4 Gruppen geteilt und für jede Gruppe wird, 
für einfacheren Zugriff, hinterinander ein 4Bit-Pwm im Array abgelegt

[Wert1.0] [Wert16.0] [Wert256.0] [Wert4096.0] [Wert1.1] [Wert16.1] ... 
[Wert1.14] [Wert16.14] [Wert256.14] [Wert4096.14]

Alle 250µs(@16MHz) wird der IRQ aufgerufen, und zwar 15 mal, danach 
gehts von vorn los.

Im IRQ werden immer 4 Werte ausgegeben:

zuerst Wert1.x  für 1Takt = 62,5ns@16MHz
dann   Wert16.x für 15Takte = ~1µs@16MHz
dann   Wert256.x für 225Takte = ~16µs@16MHz
dann   Wert4096.x bis zum neuen IRQ= ~256µs

(Die Takte und Zeiten hab ich mit delay_us experimentell eingestellt 
(weil es ja immer 15 PWM-Einheiten sind, was sich doof rechnet),
wer Lust hat kanns ja mal genau ausrechnen - aber manchmal geht 
probieren schneller als studieren)

-EIN CODE SAGT MEHR ALS 1000 WORTE-!?

//#####################################################
1
//prozessor läuft (jetzt) mit 16MHz -> 1Zyklus=62,5ns
2
3
//zum Verteilen der Bits
4
const u8 sidemask16[] PROGMEM = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7 /*, 0xf*/};
5
6
//16bit Kanalwerte
7
uint16_t Kanal[8];  //Kanalwert 0..65535
8
9
//Kanalpwm LookUp
10
uint8_t pwm_arr[60];    
11
12
13
ISR(TIMER0_COMPA_vect) //256µs@16MHz *15= 3,84ms =260Hz
14
{
15
 static uint_8 pwm=0;  //lokal and statisch
16
 static uint8_t *pp=pwm_arr; //läuft über das LookUp
17
18
 uint8_t val0=*(pp++);
19
 uint8_t val1=*(pp++); //schon mal holen damit das ausgeben nachher nur einen Takt benötigt!!
20
21
 PORTD=val0;  //maske für die bits 0..3 ausgeben steht 1Takt 62,5us@16MHz
22
23
 PORTD=val1;  //maske für die bits 4..7 ausgeben
24
 delay_us(0.9); //ca 1µs
25
26
 PORTD=*(pp++);//maske für die bits 8..11 ausgeben
27
 delay_us(14.8); // ca 15µs
28
 
29
 PORTD=*(pp++);//maske für die bits 12..15 ausgeben
30
      //bis zum nächsten irq , etwa 256µs
31
32
 if ((++pwm)==16) //nach 15 IRQs neu laden
33
 {
34
  pwm=0; //!!
35
  pp=pwm_arr;
36
 }
37
38
}
39
40
//regelmässig in Main aufrufen (oder bei Kanaländerungen)
41
void calc_pwm()
42
{
43
  //FEHLT NOCH -- EINFÜGEN
44
 uint8_t pwmpos;
45
 
46
 for (pwmpos=0; pwmpos<15; ++pwmpos)
47
 {
48
  uint8_t pwmmap=sidemask16[pwmpos];  //ähhm: naja, der wert halt den die Kanal-bit-gruppe haben muss um an dieser stelle ein Bit zu setzen..phuu
49
50
  uint8_t valo=0,val1=0,val2=0,val3=0;  //Die nach Aktiv-Zeit sortierten PORT-Bitmasken
51
  
52
  uint8_t chn;
53
54
  for (chn=0; chn<8; ++chn)
55
  {
56
   if ( (kanal[chn]&0x0f)> pwmmap)  //bits0..3
57
    val0|=1<<chn;
58
   
59
   if ( ((kanal[chn]&0x0f)>>4) > pwmmap)//bits4..7
60
    val1|=1<<chn;
61
   
62
   if ( ((kanal[chn]&0x0f)>>8) > pwmmap)//bits8..11
63
    val2|=1<<chn;
64
   
65
   if ( ((kanal[chn]&0x0f)>>4) > pwmmap)//bits12..15
66
    val3|=1<<chn;
67
  }
68
  pwm_arr[pwmpos<<2]=val0;
69
  pwm_arr[pwmpos<<2+1]=val1;
70
  pwm_arr[pwmpos<<2+2]=val2;
71
  pwm_arr[pwmpos<<2+3]=val3;
72
 }
73
74
}

So, sieht schon ganz ordentlich aus; der IRQ ist etwas länger wird aber 
nur noch alle 256µs aufgerufen.
Man könnte nun auch für mehr Ports die Daten aus anderen LookUps 
ausgeben.
Calc_PWM muss übrigens nur aufgerufen werden wenn sich ein Kanalwert 
geändert hat und hat ausserdem noch reichlich Optimierungspotential.

Das verteilen der Bits auf das Feld übernimmt hier Calc_pwm auch schon 
mit.

//#####################################################

---BASTELECKE---

Aber erst mal noch ein Gedankenspiel: Wenn mann die Idee mit den 
Bitgruppen weiterverfolgt kommt man noch zu einer anderen Variante die
hier keinesfalls verschwiegen werden soll.

1.Variante 16Bit*1 =Ein Feld mit 65536 Bytes die nacheinander ausgegeben 
Werden (Purer denkfauler speicherundrechenzeitfresserluxus)
2.Variante 8Bit*2  =2Felder mit je 255 Bytes für jeweis immer 1 
Zeitanteil und 255 Zeitanteile
3.Variante 4Bit*4  =4Felder mit je 16 Bytes für je 1,15,225 und 3375 
Zeitanteile

4.Variante 1Bit*16 =16Felder, Je 1Byte mit je 1Takt,2Takte 
4Takte,8Takte,16Takte,32Takte...?? geht das
1
//16bit Kanalwerte
2
uint16_t Kanal[8];  //Kanalwert 0..65535
3
4
//Kanalpwm LookUp
5
uint8_t pwm_arr[16];    //byte0 ist jeweils Bit0 vom Kanalx, byte1 ist jeweils Bit1 vom Kanalx...
6
7
ISR(TIMER0_COMPA_vect) //
8
{
9
 static uint_8 pwm=0;  //lokal and statisch
10
 static uint8_t *pp=pwm_arr;
11
12
 if (pwm++<10)//zB
13
 {
14
  val0=*(pp++);
15
  val1=*(pp++);//schon mal holen damit das ausgeben nachher nur einen Takt benötigt!!
16
  val2=*(pp++);
17
  val3=*(pp++);
18
19
  PORT=val0; //62,5µs@16MHz Bit0
20
21
  PORT=val1;
22
  PORT=val1; //125µs@16MHz Bit1
23
24
  PORT=val2;
25
  PORT=val2;
26
  PORT=val2;
27
  PORT=val2; //250µs@16MHz Bit2
28
29
  PORT=val3;
30
  PORT=val3;
31
  PORT=val3;
32
  PORT=val3;
33
  PORT=val3;
34
  PORT=val3;
35
  PORT=val3;
36
  PORT=val3; //500µs@16MHz BIT3
37
38
  PORTD=pwm_arr[*(pp++)];
39
  delay_us(~.9); // ca 1µs@16MHz BIT4
40
41
  PORTD=pwm_arr[*(pp++)];
42
  delay_us(~1.8); // ca 2µs@16MHz BIT5
43
44
  PORTD=pwm_arr[*(pp++)];
45
  delay_us(~3.6); // ca 4µs@16MHz BIT6
46
47
  PORTD=pwm_arr[*(pp++)];
48
  delay_us(~7.2); // ca 8µs@16MHz BIT7
49
50
  PORTD=pwm_arr[*(pp++)];
51
  delay_us(~15); // ca 16µs@16MHz BIT8
52
53
  PORTD=pwm_arr[*(pp++)]; //Bit 9
54
55
  //
56
  !-Restart Timer mit 32µs
57
  //
58
 }
59
 else
60
 {
61
  PORTD=pwm_arr[*(pp++)]; //Bit 10..15
62
  //
63
  !-Restart Timer mit 
64
   62µs für Bit10 
65
   125us für Bit11
66
   250us für Bit12
67
   500us für Bit13
68
   1ms für Bit14
69
   2ms für Bit15
70
  //
71
  if ((++pwm)==16) //nach 15 IRQs neu laden
72
  {
73
   pwm=0; //!!
74
   pp=pwm_arr;
75
  }
76
77
}
78
79
void calc_pwm()
80
{
81
 uint8_t pwmpos;
82
 
83
 for (pwmpos=0; pwmpos<16; ++pwmpos)
84
 {
85
  val=0;
86
  
87
  for (chn=0; chn<8; ++chn)
88
  {
89
   if (kanal[chn]&(1<<pwmpos))
90
    val|=1<<chn;
91
  }
92
  pwm_arr[pwmpos]=val;
93
 }
94
}

Sehr sparsame Variante. Je nachdem wie weit man den 1.IRQ ausweiten kann 
(kürzer würd ich nicht machen) könnte man noch Bit 10 oder auch 11 mit 
oben

reinnehmen. Ich hab meist noch DMX zu empfangen und/oder zu senden und 
da sind u.U, alle 44µs Interrupts fällig, also denk ich sind 32us ein 
guter
Mittelweg.

Der Timer muss immer nachgestellt werden, ist eben etwas Tricky..
Ein Nachteil (wenn es einer ist) bei dieser Variante: man kann die Bits 
nicht gleichmässig über die IRQs verteilen und die PWM-Frequenz liegt 
fest.
(na gut sind hier ca 250Hz, aber wenn man mit dem Blick über eine Wand 
mit LEDs huscht sieht manns evl doch |-) )

//#####################################################

 Es gibt natürlich noch viel mehr Varianten z.B. für eine höhere oder 
niedrigere Auflösung und dem Experimetierfreudigen sei hiermit ein 
kleiner Anstoss zuteil geworden sein...;-}

//EOF ;-}

von Julian W. (julian-w) Benutzerseite


Lesenswert?

Hallo,
ist zwar schon etwas alt der Thread, aber mach hoffe ich mal nix.

Hat evtl. einer einen fertigen Code-Schnippsel, der z.B. auf einem 
Atmega8 laufen würde?

von Willnich (Gast)


Lesenswert?


von Julian W. (julian-w) Benutzerseite


Lesenswert?

Ja gut, aber da stehen einem nur 8bit zur Verfügung. Da ich momentan mit 
RGB-LED's experimentiere, sind mir 8bit etwas wenig, vor allem in den 
"dunkleren" Bereichen...

von DerDaOben (Gast)


Lesenswert?

Thomas Ahlendorf schrieb:
> -EIN CODE SAGT MEHR ALS 1000 WORTE-!?

Ja hast recht: KOT

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.