Forum: Mikrocontroller und Digitale Elektronik C-Code für Software TWI für Atmega gesucht


von Michael (Gast)


Lesenswert?

Hallo zusammen,

ich bin auf der Suche nach einen funktionierenden Beispielcode in C, um 
mit einem Atmega168 einen Temperatursensor (MCP9804) per Software-I2C 
auszulesen. Per Hardware-TWI funktioniert es schon, nur brauche ich den 
HW-TWI des Atmega für die Verbindung zu einem anderen Master-uC.
In der Codequelle (Beitrag "I2C-Master [ohne TWI (Softwarelösung) für die ATMEGAs") habe 
ich einen Code gefunden, leider funktioniert er bei mir nicht. Laut 
diesem Thread (Beitrag "LM75 mit Software TWI auslesen (Atmega 16, C)") ist der Code 
anscheindend auch nicht ganz fehlerfrei. Ich habe leider kein Oszi zur 
Hand, nur einen AVR-Dragon und eine UART-Verbindung zum debuggen.
Für Assembler gibt es ja so einige Beispiel (Peter Fleury) nur leider 
finde ich keine Code für AVR-Controller.

Ich verwende den GCC-Compiler und das AVR-Studio als 
Entwicklungsumgebung.

Danke

von Michael (Gast)


Angehängte Dateien:

Lesenswert?

Kann ich die Peter Fleury Bibliothek 
(http://homepage.hispeed.ch/peterfleury/group__pfleury__ic2master.html) 
insofern nutzen, dass die main-Funktion in C-Code die Funktionen, die in 
Assembler-Sprache geschrieben sind, aufruft ?
Im Anhang hab ich das Zip-File nochmal hinzugefügt.

von Michael (Gast)


Lesenswert?

Ich habe jetzt das Projekt von Peter Fleury übernommen und nun habe nun 
folgendes Problem:

...
i2c_start_wait(L_Device_ADDRESS+I2C_WRITE);     // set device address 
and write mode
     i2c_write(L_Sensor_ADDRESS_MSB_BYTE);                        // 
write address = 5
     i2c_stop();
     i2c_start_wait(L_Device_ADDRESS+I2C_READ);       // set device 
address and read mode
     ret = i2c_readNak();
     USART_Transmit(???);
     i2c_stop();
...

Ich will das gelesene letzte Byte aus einen Sensor über die 
UART-Schnittstelle auf einen Terminalprogramm ausgeben, und nun weiß ich 
nicht, was ich anstatt der drei Fragezeichen als Variable einfügen soll.
Gebe ich ret als Variable ein, so bleibt der Wert konstant auf Null. 
i2c_readNak hat ja als Ausgabe entweder 0 oder 1, oder ?
Was bedeutdet r24 ? Ist das eine Adresse auf den Wert ?
Beim Hardware TWI gebe ich einfach TWDR als Variable ein, und es wird 
der Sensorwert übergeben.
Unten steht nochmal der Kommentar zur i2c_readNak -Funktion.
Kann mir da bitte jemand weiterhelfen ?

;*********************************************************************** 
**
; read one byte from the I2C device, send ack or nak to device
; (ack=1, send ack, request more data from device
;  ack=0, send nak, read is followed by a stop condition)
;
; extern unsigned char i2c_read(unsigned char ack);
;  ack = r24, return = r25(=0):r24
; extern unsigned char i2c_readAck(void);
; extern unsigned char i2c_readNak(void);
;   return = r25(=0):r24
;*********************************************************************** 
**

von Oliver J. (skriptkiddy)


Lesenswert?


von Michael (Gast)


Lesenswert?

Danke für die schnelle Antwort.

Mein Terminalprogramm kann aber doch alle Formate lesen, warum muss ich 
das dann noch wandeln ?

In der "ret"-Variable steht also sicher der ausgelesene Wert ?

Ich habe leider gleich noch eine Frage: Unter i2cmaster.S steht 
folgendes:

;-- map the IO register back into the IO address space
#define SDA_DDR    (_SFR_IO_ADDR(SDA_PORT) - 1)
#define SCL_DDR    (_SFR_IO_ADDR(SCL_PORT) - 1)
#define SDA_OUT    _SFR_IO_ADDR(SDA_PORT)
#define SCL_OUT    _SFR_IO_ADDR(SCL_PORT)
#define SDA_IN    (_SFR_IO_ADDR(SDA_PORT) +1)
#define SCL_IN    (_SFR_IO_ADDR(SCL_PORT) +1)

Was bedeudet das ? Unter iom8x.h steht als als Registeradresse des PORTD 
(Pin 7 soll SDA und Pin6 soll SCL sein) der Wert _SFR_I08 (0x0B). Hat 
das damit zu tun ? Ist das im Datenblatt (ich verwende den Atmega168) 
beschrieben ? Unter welchen Stichwort suche ich da am Besten ?

von Oliver (Gast)


Lesenswert?

Michael schrieb:
> Ist das im Datenblatt (ich verwende den Atmega168)
> beschrieben ? Unter welchen Stichwort suche ich da am Besten ?

Kapitel "Register summary"

Die IO-Register des AVR liegen auch alle im Adressspace. Die Makros 
berechnen die Adressen für alle benötigten Register.

An den Zeilen brauchst du aber nichts zu ändern, nur an denen davor.

Oliver

von Michael (Gast)


Lesenswert?

Ich hab den Wert in den beiden unteren Zeilen schon jeweils von -2 auf 
+1 abgeändert. Ich habe das aus diesem Thread entnommen: 
(http://8515.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=100373)

Noch eine Sache verwundert mich ein wenig. In der main-Funktion habe ich 
den Ausdruck

if(!(i2c_start(L_Device_ADDRESS +I2C_WRITE)))//Slave bereit 
zumschreiben?
{...
 }
else
{
  i2c_stop();
}

hinzugefügt, um festzustellen, ob die Adresse des devices richtig 
übertragen und mit einem ACK bestätigt wurde. Nun ist es leider so, dass 
das Programm, selbst wenn ich eine falsche device_Adresse eingebe, 
dennoch nicht in den error Zusatnd wechselt. Selbst wenn ich die den 
SDA-Leitung vom Controller trenne, geht er nicht in den Fehlermodus.
Woran kann das liegen ?

von Oliver (Gast)


Lesenswert?

Michael schrieb:
> Ich hab den Wert in den beiden unteren Zeilen schon jeweils von -2 auf
> +1 abgeändert. Ich habe das aus diesem Thread entnommen:
> (http://8515.avrfreaks.net/index.php?name=PNphpBB2&;...)

Na ja, in dem verlinkten Thread geht es um einen ATXmega128A1, du hast 
eine Mega168. Vielleicht schaust du tatsächlich mal ins Datenblatt. Denn 
ansonsten ist deine Aktion ziemlich sinnfrei.

Oliver

von Michael (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe die Werte aus den genannten Thread genommen, weil nur mit 
dieser "Zahlenkombination" auf meinem UART was ankommt. Leider das 
falsche, weil ja wie oben erwähnt diese Abfrageschlaufe ständig als O.K. 
abgehadnelt wird, obwohl die device-Adresse falsch ist, oder überhaupt 
nix an SDA angelötet ist. Anderseits funktioniert auch bei richtiger 
Adressierung des devices bei Verwendung der +2 im Makro der Code nicht, 
d.h. das Programm spingt nicht in die Schleife, obwohl er es eigentlich 
sollte. Per Hardware-TWI funktioniert alles, am Sensor kann es also auch 
nicht liegen.
Im Anahng habe ich mal mein Projekt angefügt.

von Oliver (Gast)


Lesenswert?

Nimm den Originalcode. Der funktioniert mit ATMegas, nur für ATXmegas 
müssen die Adressenoffsets geändert werden.

Warum auch immer dein Code nicht funktioniert, an den Offsets liegt es 
nicht.

Oliver

von Michael (Gast)


Lesenswert?

In Ordnung, dann kann ich schon mal das als Fehlerquelle ausschließen.

Im Moment stellt es sich so dar, dass die start condition vom 
Slave-Sensor nicht beantwortet wird. Ich habe jetzt schon den zweiten 
Sensor am Controller (der mit Hardware-TWI auch funktioniert hat).
Ich weiß nicht mehr, woran es noch liegen kann. Ich kann schon 
irgendeinen unbesetzten PORT-Pin für die Kommunikation nutzen, oder ?

Prinzipiell funktioniert das Programm schon, oder ? Hat das von euch 
schon mal jemand ausprobiert ?

von Rene K. (draconix)


Lesenswert?

Schreib dir doch selbst eine Routine?!

Ich habe mir damals für nen LM75 mit I2C folgendes geschrieben....:

1
#define DS75_SDA PORTB.B5
2
#define DS75_PinDATA PINB.F5
3
#define DS75_SCL  PORTB.B4                           
4
5
6
void DS75_setWRITE(void){
7
  DDRB += 0x20;
8
}
9
10
void DS75_setREAD(void){
11
  DDRB -= 0x20;
12
}
13
14
void DS75_Start(){
15
     DS75_setWRITE();
16
     DS75_SDA = 1;
17
     DS75_SCL = 1;
18
     asm nop;
19
     DS75_SCL = 1;
20
     DS75_SDA = 0;
21
     asm nop;
22
     DS75_SCL = 0;
23
     DS75_SDA = 0;
24
     asm nop;
25
     DS75_setREAD();
26
}
27
28
void DS75_Stop(){
29
     DS75_setWRITE();
30
     DS75_SCL = 0; asm nop;
31
     DS75_SDA = 0; asm nop;
32
     DS75_SCL = 1; asm nop;
33
     DS75_SDA = 1; asm nop;
34
     DS75_setREAD();
35
}
36
37
unsigned short DS75_Write(unsigned short WTR) {
38
   short i, error = 0;
39
40
   DS75_setWRITE();
41
   for(i=8;i>0;i--)
42
   {
43
     DS75_SCL = 0;                                           mdelay;
44
     if((WTR >> (i-1)) & 1)  DS75_SDA = 1; else DS75_SDA = 0;     mdelay;
45
     DS75_SCL = 1;                                           mdelay;
46
   }
47
48
  DS75_setREAD();
49
50
  DS75_SCL = 0;                                              mdelay;
51
  DS75_SCL = 1;                                              mdelay;
52
  error = DS75_PinDATA;                                      mdelay;
53
  DS75_SCL = 0;                                              mdelay;
54
55
  return error;
56
}
57
58
int DS75_Read_Hex(unsigned short ack) {
59
  unsigned short i;
60
  unsigned short test = 0;
61
  DS75_SCL = 0;
62
63
  for(i=8;i>0;i--)
64
  {
65
     DS75_SCL = 1;  asm nop;
66
     if(DS75_PinDATA == 1) test |= ((unsigned long)1 << (i-1));
67
     DS75_SCL = 0;  asm nop;
68
  }
69
70
  if(ack == 1)
71
  {
72
      DS75_setWRITE();
73
      DS75_SDA = 0; asm nop;
74
      DS75_SCL = 1; asm nop;
75
      DS75_SCL = 0; asm nop;
76
  } else {
77
      DS75_setWRITE();
78
      DS75_SDA = 1; asm nop;
79
      DS75_SCL = 1; asm nop;
80
      DS75_SCL = 0; asm nop;
81
  }
82
83
  DS75_setREAD();
84
  return test;
85
}

von Michael (Gast)


Lesenswert?

Ich kann leider kein Assembler, weswegen ich gehofft habe, eine fertige 
Routine zu übernehmen. Die Bibliothek aus diesem link hier
(Beitrag "I2C-Master [ohne TWI (Softwarelösung) für die ATMEGAs") hat bei mir leider auch 
nicht funktioniert.
Ich bin auch allgemmein nicht so fit in der C-Programmierung, sodass ist 
hier mehr oder weniger auf fertige Funktionen angewisen bin.

Es kann eigentlich nur eine Kleinigkeit sein, aber komm ich nicht drauf.

von Rene K. (draconix)


Lesenswert?

Michael schrieb:
> Ich kann leider kein Assembler, weswegen ich gehofft habe, eine fertige
> Routine zu übernehmen.

Das obige ist auch kein Assembler. Ist C - kein feines C, geb ich ja zu 
- aber es funktioniert.

im Grunde wird die funktion aus dem Hauptprogramm folgendermaßen 
aufgerufen:
1
unsigned int ret;
2
3
DS75_Start();
4
DS75_Write(0x9f);   // Die Slaveadresse
5
ret = DS75_Read(0); // auslesen 1 für ACK und 0 für NoACK vom Master
6
DS75_Stop();

Klar die Namen der Routinen und die Portzuweisungen müssen / können 
natürlich angepasst werden.

von Michael (Gast)


Angehängte Dateien:

Lesenswert?

Danke für den Beispielcode in C, Rene, aber ich denke ich versuchs doch 
noch erstmal mit der Lib vom Peter Fleury.

Ich habe jetzt die compiler optimziation auf 0s (so wie's auch im 
makefile angegeben ist) verändert, und noch zwei Pullup-Widerstände mit 
4,7k eingelötet, obwohl seitens am Sensor schon Pullup-Widerstände 
eingebaut sind. Im Beispielcode habe ich noch eine if-Abfrage eingebaut, 
um festzustellen, ob die Start Anweisung am Sensor angekommen ist. Laut 
Programm kommt die Start-Anweisung vom Sensor mit einem Ack bestätigt 
an. Das Programm hüpft nämlich in die if-Schleife. Das Problem ist nur, 
dass es das auch tut, wenn ich eine falsche Adresse für den 
Sensor-device eingebe. Als Sensorwert (hier das LSB-Byte von einem 
Lichtsensor) wird immer die Zahl 52 (Dez.) übertragen, der sich leider 
nicht mit der Lichteinstrahlung auf den Sensor ändert. Bei einem 
Temperatursensor und gleichen (an Sensor angepassten Programm) wird auch 
diese Zahl 52 übertragen.
Hat hier vielleicht jemand eine Erklärung dafür ?

von Oliver J. (skriptkiddy)


Lesenswert?

Auch wenn du schon fast fertig bist; dieser schlanke Code hat sich bei 
mir schon oft bewährt:
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=77455

Gruß Skriptkiddy

von Michael (Gast)


Angehängte Dateien:

Lesenswert?

Danke für den Code,
ich hab ihn gleich mal ausprobiert (an einem Temperatursensor MCP9804) 
und leider erscheint als Wert nur ständig 0xFF. Ich habe einen 4Mhz 
Quartz am Mastercontroller in Verwendung. Die internen Delay-Zeiten 
ändern sich ja dadurch. Kann es daran liegen, warum es nicht 
funktioniert ?  Am HW-TWI funktioniert es mit dem gleichen Ablauf der 
Befehle.
Eine I2C_init Routine gibt's im Code auch nicht. Braucht mann das gar 
nicht unbedingt ?

Pin PB0 soll mein SDA, PB0 der SCL Anschluss sein. Im Anhang habe ich 
mein Projekt mal angehängt. Egal welche Register ich auch auslesen will, 
es wird immer nur die 0XFF übertragen.

von Oliver (Gast)


Lesenswert?

Die Routinen von Peter Fleury funktionieren bei mir einwandfrei.

Hardwareprobleme kannst du ausschließen?

Oliver

von Michael (Gast)


Lesenswert?

Ich kann sie insofern aussschließen, dass die Kommunikation über den 
Hardware TWI einwandfrei funktioniert.
Auch mit verschiedenen Sensoren.

von Oliver (Gast)


Lesenswert?

An den selben Pins? Hardware-TWI geht, Software nicht?

Da steht folgender Satz in der Doku:

>Adapt the SCL and SDA port and pin definitions and eventually the delay >routine 
in the module i2cmaster.S to your target when using the software >I2C 
implementation !

Die delay-Routine in i2cmaster.S ist für 4MHz ausgelegt. Wenn dein 
Prozessor auch mit 4MHz läuft, sollte es ja passen. Trotzdem kannst du 
ja mal ausprobieren, was passiert, wenn du vor dem ret noch ein paar nop 
einfügst.

Oliver

von Michael (Gast)


Lesenswert?

Ich habe die beiden "Software" Anschlüsse SDA und SCL auf verschiedene 
Portpins gelegt. (PC0/PC1, PD0/PD1) und entsprechend den Assembler-Code 
angepasst. Leider mit den gleichen enttäuschenden Ergebnis. Ich hatte 
auch erst einen 4Mhz Quarz am Controller und dann einen 8Mhz Quarz mit 
entsprechender Codeanpassung laut diesen Thread 
(Beitrag "I2C, es tut sich nichts an den Leitungen", ganz unten) ausgetestet.

Was meinst du, Oliver, mit den "nops" vor den "ret" ? An welcher Stelle 
des Programmcodes soll ich da noch etwas einfügen ?

von Oliver (Gast)


Lesenswert?

Michael schrieb:
> Was meinst du, Oliver, mit den "nops" vor den "ret" ? An welcher Stelle
> des Programmcodes soll ich da noch etwas einfügen ?

Mit den nops meinte ich das gleiche, was auch in dem von dir verlinkten 
Thread steht.

Michael schrieb:
> Ich habe die beiden "Software" Anschlüsse SDA und SCL auf verschiedene
> Portpins gelegt. (PC0/PC1, PD0/PD1)

Warum 4 Pins?
Was passiert, wenn du Software-TWI auf den Hardware-TWI-Pins laufen 
lässt?
Pull-Up-Widerstände (4,7k) hast du selbstverständlich überall dran, 
oder?
Wie steht die CKDIV8-Fuse?

Oliver

von Michael (Gast)


Lesenswert?

Ich habe entweder den Slave an PC0/PC1 oder PD0/PD1 angeschlossen. Ich 
hab's nur an verschiedenen Portpins ausprobiert.

Die Software TWI habe ich an den Hardware Pins angeschlossen (das 
Assembler File entsprechend abgeändert) und leider habe ich das selbe 
Ergebnis, der Slave gibt keie Antwort.

Die beiden Sensordatenleitungen SDA7SCL sind schon mit einen 4,7k 
Widerstand versehen. Beim Controller noch extra zwei Pullups einzubauen 
macht ja dann keinen Sinn mehr, oder ? Wobei ich es auch ausgetestet 
habe, und am Ergebnis hat sich auch nichts geändert.

Beim CKDIV8-Fuse ist im AVR-Studio kein Häkchen. Ich hab nun testweise 
ein Häkchen dorthin gesetzt und im Terminalprogramm kommt anstatt ein 
"A" für Fehler ein ganz anderes Zeichen raus. Es ist aber immernoch der 
Fehlerfall der durchlaufen wird.

von Oliver (Gast)


Lesenswert?

Tja, dann musst du dich da weiter durchwühlen.

Wenn du kein Speicheroszi hast, auch hier mit nicht weiterkommst:
Beitrag "I2C (TWI) Sniffer mit AVR"
und auch keinen PC mit Soundkarte hast, den du als "Soundkartenoszi" 
nutzen könntest, dann häng doch zumindest mal eine LED an SCL, um zu 
schauen, ob sich da überhaupt was tut. Nutz den Dragon zum debuggen, und 
versuch halt rauszufinden, was da schief geht.

Oliver

von Michael (Gast)


Lesenswert?

Ich habe nun mal LED's mit an die Portpins angelötet, um zu sehen, ob 
sich überhaupt was tut. Ich debugge über SPI mit Hilfe des AVR-Dragons.
Leider ziehen die LEDs ein Signal, dass an Port anliegt (PINxx) sofort 
nach unten. Die LEDs ziehen aber max. nur 25mA pro Portpin. Es leuchtet 
als leider gar nichts. Sobald ich die LEDs wieder weglöte, erkennt man 
beim Debuggen, dass das Programm die Portpins schon entsprechend nach 
oben oder unten zieht.
Wie kann es denn sein, dass die LEDs den PORT in die Knie zwingen ?

Außerdem ist mir noch aufgefallen, dass das Programm eigentlich schon am 
Anfang des Programms bei i2c_init hängen bleibt und nicht mehr die 
Funktion verlässt. In Wirklichkeit läuft das Programm ja aber weiter, 
weil ansonst ja auch keine Fehlermeldung erscheinen würde.

von Oliver (Gast)


Lesenswert?

TWI ist low-active, der Pegel wird nur über die Pull-Ups nach VCC 
gezogen, und die haben 4,7k. Da fliesst nicht viel.
Also müssen die LED's vom Pin nach VCC, mit passendem Vorwiderstand. Die 
Leuchten dann, wenn der Pin aktiv = low ist.

25mA für eine LED sind aber selbst für normale Anwendungen schon sehr 
viel. Ich würde da nicht mehr als ein paar mA fliessen lassen, keine 
Ahnung, was so ein LM75 an maximalem Strom verträgt. Es reicht ja, wenn 
die LED so gerade erkennbar glimmt.

Michael schrieb:
> Außerdem ist mir noch aufgefallen, dass das Programm eigentlich schon am
> Anfang des Programms bei i2c_init hängen bleibt und nicht mehr die
> Funktion verlässt. In Wirklichkeit läuft das Programm ja aber weiter,
> weil ansonst ja auch keine Fehlermeldung erscheinen würde.

???

Was meinst du mit "in Wirklichkeit?" i2c_init bleibt hängen, wenn der 
Slave nicht reagiert (oder gar keiner da ist). Da aber im lm75 
vermutlich time-outs vorhanden sind, dürfte das im Einzelschrittmodus 
nicht funktionieren. Setz mal einen Breakpoint, und lass das Programm 
von Anfgang an bis dahin durchlaufen. Wenn es klappt, setze den 
breakpoint weiter nach hinten, und starte erneut.

Oliver

von Michael (Gast)


Lesenswert?

O.K., dass debuggen mit den breakpoints probiere ich gleich mal aus.
Ich muss noch sagen, ich will nicht den LM75 ansteuern, sondern einen 
Temperatursensor MCP9804 von Microchip bzw. einen Lichtsensor von 
Intersil namens ISL29010.

Das ich den Sensor mit 3V und den Mikrocontroller mit 5V betreibe stellt 
aber kein Problem dar, oder ?

von Oliver (Gast)


Lesenswert?

Michael schrieb:
> Das ich den Sensor mit 3V und den Mikrocontroller mit 5V betreibe stellt
> aber kein Problem dar, oder ?

Laut Mega-Datenblatt muß der high-Pegel mindestens 0.6*VCC betragen, um 
sicher erkannt zu werden, das sind 3V. Wenn du die Pull-ups gegen 3V 
schaltest, sollte der Mega das also als high erkennen, was mit 
Hardware-TWI ja anscheinend auch funktioniert. Das ist aber einfach 
nachzuprüfen.

Schaltest du die pull ups gegen 5V, steht im Datenblatt der Sensoren, ob 
die das vertragen. Ich schätze mal, die tun das nicht.

Oliver

von Michael (Gast)


Lesenswert?

Es funktioniert jetzt.

Der Grund war die falsche Adressierung des Sensors. Die Adresse musste 
nur um ein Bit linksgeshifted werden. In diesem thread ist es kurz 
beschrieben: 
(http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=625681).
Danke für eure Hilfe !

von Oliver (Gast)


Lesenswert?

Michael schrieb:
> Ich kann sie insofern aussschließen, dass die Kommunikation über den
> Hardware TWI einwandfrei funktioniert.
> Auch mit verschiedenen Sensoren.

Michael schrieb:
> Es funktioniert jetzt.
>
> Der Grund war die falsche Adressierung des Sensors. Die Adresse musste
> nur um ein Bit linksgeshifted werden.

Ja nee, is klar. Und wieder mal sinnlos Zeit verplempert.

Der Problem mit der Positionierung der 7-bit-Adresse im Byte ist nicht 
neu, sogar naheliegend, aber in Peter Fleury's lib in der Hardware- und 
in der Softwareversion gleich. Wenn es also mit der Harwareversion 
läuft, kann es das nicht gewesen sein. Was genau verstehst du also unter 
"laufen"?

Leicht angepisst...

Oliver

von Michael (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe für den Hardware-TWI die Funktionsbibliothek von Manfred 
Langemann verwendet, die jedoch auf der Bib von Peter Fleury beruht.
Hier ist bei der Adressierung kein Linksshift notwendig.

Entschuldige Oliver, dass hätte ich als Fehlerquelle mit etwas 
nachdenken und nachschauen schon ausschließen können.

von Oliver (Gast)


Lesenswert?

Na dann, Hauptsache, es funktioniert jetzt.

Oliver

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.