Hallo! Ich habe ein Problem mit meinem Programm. Ich möchte vom PC eine 1 senden und dann soll auf dem Board die LED ausgehen, doch es passiert nichts. Kann sich einer mal den Quellcode anschauen und mir weiterhelfen? Danke! #include <avr/io.h> #include <avr/interrupt.h> #define F_CPU 1000000UL #define BAUD 9600UL #define UBRR_BAUD ((F_CPU/(16L*BAUD))-1) void uart_init(void) { UBRRH = (uint8_t) (UBRR_BAUD>>8); UBRRL = (uint8_t) (UBRR_BAUD & 0xFF); UCSRB = (1<<TXEN) | (1<<RXEN)|(1<<RXCIE); UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); } void led_init(char value) { if(value=='1') PORTA=0x01; else PORTA=0x00; } ISR(USART_RXC_vect) { led_init(UDR); } int main(void) { DDRA = 0xff; uart_init(); sei(); while(1){ } return 0; }
Funktioniert dein Programm im Pollingmodus oder 1/ hast du die üblichen Probleme mit dem internen RC-Oszillator (1. Vermutung bei 1 MHz) die erforderliche Baudratengenauigkeit für reibungslosen UART-Transfer einzuhalten? 2/ stimmt die Verbindung (TX,RX-Kreuzung) und TTL/RS232-Wandlung zwischen µC und PC nicht?
Wenn Du das U2X-Bit im UCSRA nicht setzt, kriegst Du bei 1 MHz CPU-Frequenz und 9600 Baud bereits rein rechnerisch einen Baudratenfehler von 7 %. Das ist schon mal tödlich für die Übertragung. Entweder das U2X setzen (und natürlich den Wert im UBRR entsprechend anpassen) oder eine andere CPU-Frequenz nehmen oder eine andere Baudrate. Und was den internen RC-Oszillator angeht, kann ich mich Stefan nur anschließen: Asynchrone Übertragung mit so einer Taktquelle ist generell Glückssache. BTW: Sorry, dass ich auf Deine Mail (anscheinend ja zur selben Problematik) nicht geantwortet hab. Hatte in den letzten Tagen nen Haufen Stress und hab überhaupt nicht in den Email-Account reingeschaut. Hoffe, das Problem ist damit geklärt.
Hallo! Sorry das ich erst so spät antworte, denn ich war krank! Ich habe das Programm, aber mal ohne Interrupt geschrieben und da hatte alles reibungslos funktioniert. Da hatte ich genau die gleichen Einstellungen! Kann es noch an etwas anderes liegen?
Hmm, dann gehen mir auch die Ideen aus. Eine vielleicht noch - lies mal explizit UDR, so wie es im Datenblatt beschrieben ist, statt eine Übergabe auf dem Stack zu machen.
1 | ISR(USART_RXC_vect) |
2 | {
|
3 | unsigned char tmp; |
4 | |
5 | tmp = UDR |
6 | |
7 | led_init(tmp); |
8 | }
|
Und noch eine vielleicht, toggel mal die LED in der Interruptroutine, dann siehst du, ob die überhaupt angesprungen wird. Trotzdem UDR lesen, weil es sonst den RXC Interrupt blockiert.
1 | ISR(USART_RXC_vect) |
2 | {
|
3 | volatile unsigned char tmp; |
4 | |
5 | tmp = UDR |
6 | |
7 | // led_init(tmp);
|
8 | |
9 | PORTA ^= (1<<PA0); // XOR |
10 | // http://www.mikrocontroller.net/articles/Bitmanipulation#Standard_C_3
|
11 | }
|
Hallo Stefan! Danke nochmal für deine schnelle Antwort! Ich habe alle deine Varianten durchgespielt, doch keine hat funktioniert. Es muss an dem Interrupt liegen, der wird einfach nicht ausgelöst. Warum verstehe ich aber nicht. Ich habe auch den internen Oszilator auf 8Mhz gestellt. Ohne Interrupt funktioniert die Sache einwandfrei. Gibt es noch eine andere Lösung mit einer anderen Interruptroutine?
> ISR(USART_RXC_vect)
Ich glaub, der Vektor müsste "USART_RX_vect" heißen (Zumindest lt.
Tabelle in der libc-Doku). Da ist ein "C" zuviel.
EDIT:
In der iom8535.h steht es ebenfalls ohne "C". Also müsste
1 | ISR(USART_RX_vect) |
2 | {}
|
funktionieren.
Danke für deine Antwort. Du hattest recht! Jetzt funktioniert alles. Ne Frage habe ich aber trotzdem noch. Wenn du dir oben mal die led_init ansehen kannst, da steht drin das wenn ich 1 an mikrocontroller sende soll die LED ausgehen. Wie schreibe ich es aber wenn ich CP001201 senden möchte?
Die LED soll angehen, wenn "CP001201" beim µC ankommt? Da musste erstmal nen String-Vergleich machen. C kennt keinen Datentyp "string". Da müsstest Du die Funktion strncmp aus der string.h benutzen.
Ja bei diesem Fall soll die LED angehen. Kannst du es mir vielleicht an meinem obigen Beispiel zeigen. Ich bin noch ein Anfänger und habe nicht so die Ahnung von einem String-Vergleich.
Zunächst mal musst Du den Befehls-String empfangen und in einem Buffer-Array speichern. Den Vergleich kannst Du im Prinzip direkt in der if-Abfrage machen. Dazu muss man wissen, dass strncmp bei Gleichheit der verglichenen Strings eine 0 zurückgibt. Die Abfrage könnte also so aussehen:
1 | if(!strncmp(buffer, "CP001201", 9)) |
2 | PORTA = 0x01; |
Schau Dir am besten in der AVR-libc-Doku die Erläuterungen zur Funktion an. Da steht wie's geht...
Zuallererst schaltest du die LED nicht mehr in der Interrupt Routine. Deine Interrupt Routine hat nur noch die Aufgabe die eingehenden Zeichen zu sammeln und in einem char-Array bereitszustellen. Erst nach Erhalt eines Trennzeichens erkennt die ISR dass damit der Text komplett übertragen wurde und setzt seinerseits eine gloable Variable auf 1. An dieser 1 erkennt dann die Hauptschleife in main(), dass ein kompletter Text empfangen wurde und bearbeitet den Text. Zb. Dadurch dass sie Stringvergleiche macht um herauszufinden, welches Kommando übertragen wurde. Du hast hier also eine Aufgabentrennung. Die UART - ISR ist nur noch dafür zuständig ein Kommando zu empfangen und das komplette Kommando zur weiteren Bearbeitung zur Verfügung zu stellen. Was dieses Kommando bewirken soll, ist nicht mehr das Bier der ISR. Sagen wir mal, du hast dich für das Zeichen ';' entschieden um anzuzeigen, dass hier ein Kommando zu Ende ist. Dann macht deine ISR zb. das hier
1 | volatile uint8_t HabeKommando; |
2 | uint8_t KommandoCnt; |
3 | char Kommando[128]; |
4 | |
5 | ISR(USART_RX_vect) |
6 | {
|
7 | char c = UDR; |
8 | |
9 | if( c == ';' ) { |
10 | Kommando[KommandoCnt] = '\0'; |
11 | HabeKommando = 1; |
12 | KommandoCnt = 0; |
13 | }
|
14 | |
15 | else { |
16 | Kommando[KommandoCnt] = c; |
17 | KommandoCnt++; |
18 | }
|
19 | }
|
20 | |
21 | |
22 | int main() |
23 | {
|
24 | ....
|
25 | |
26 | |
27 | while( 1 ) { |
28 | |
29 | if( HabeKommando == 1 ) { |
30 | HabeKommando = 0; |
31 | |
32 | if( strcmp( Kommando, "abcd" ) == 0 ) { |
33 | // Kommando "abcd" erkannt
|
34 | }
|
35 | |
36 | ....
|
37 | }
|
38 | }
|
39 | }
|
Zur Einführung in die Stringverarbeitung empfehle ich die Lektüre eines C-Buches deiner Wahl. Das ist ein recht umfangreiches Kapitel. Zur Not findest du das wichtigste aber auch hier: http://www.mikrocontroller.net/articles/FAQ#Wie_funktioniert_String-Verarbeitung_in_C.3F
Hallo, ich habe hier mein fertiges Exemplar mit deiner Variante versucht zu vereinen. Jetzt wollte ich wissen ob das so funtionieren kann? Kannst du mir nochmal genau erklären, was der Quelltext in der ISR und in der while Schleife genau macht? #include <avr/io.h> #include <avr/interrupt.h> #include <stdio.h> #include <string.h> #define F_CPU 8000000UL #define BAUD 9600UL #define UBRR_BAUD ((F_CPU/(16L*BAUD))-1) char buffer[10]; uint8_t position; volatile uint8_t habekommando; void uart_init(void) { UBRRH = (uint8_t) (UBRR_BAUD>>8); UBRRL = (uint8_t) (UBRR_BAUD & 0xFF); UCSRB = (1<<TXEN) | (1<<RXEN)|(1<<RXCIE); UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); } void led_init { if(strcmp (buffer, "CP001201E") == 0) { PORTA=0x01; } else if(strcmp (buffer, "CP001200E") == 0) { PORTA=0x00; } } ISR(USART_RX_vect) { char c = UDR; if (c == 'E') { buffer[position] = '\0'; habekommando = 1; position = 0; led_init(UDR); } else { buffer[position] = c; position++; } } int main(void) { DDRA = 0xff; uart_init(); sei(); while(1) { if (habekommando == 1) { habekommando = 0; } } return 0; }
OT: Karl Heinz: Dein Code ist wie immer vorbildlich! Du musst wissen, ich bin Code-formatierungs-Fetischist (Sprich Wenn Variablen aus mehreren Worten bestehen jedes Wort am Anfang groß schreiben, abundzu auch mal zwei Linefeeds machen, schön überall Leertasten an den kritischen Stellen) Was ich mir noch angewöhnt habe, ist g_ als Prefix für globale Variablen (wie m_ für Membervariablen bei C++) Ich wünsche manchmal, dass der ein oder andere Lehrer des Faches "Programmieren" uns sowas vorlegt. (Quasi als gutes Beispiel). Aber Fehlanzeige. Stattdessen bekommen wir unformatierte Klassendeklarationen (Keine durchgängige Schreibweise von Variablen (Groß/Kleinschreibung gemischt) und so weiter).
> ich habe hier mein fertiges Exemplar mit deiner Variante versucht zu > vereinen. Jetzt wollte ich wissen ob das so funtionieren kann? Teil des Programmiererlebens ist es, Dinge auszuprobieren. Normalerweise kann ja nichts kaputtgehen, die Dinge funktionieren halt einfach nicht. > Kannst du mir nochmal genau erklären, was der Quelltext in der ISR und > in der while Schleife genau macht? Das ist ein 5-Zeiler. Den solltest du schon selbst analysieren können. Nimm dir Papier und Bleistift und analysiere was da passiert. Wie geht das? Du spielst Computer. Dein Papier ist dein Speicher auf dem du Variablen erzeugst und Werte zuweist bzw. von wo du die Werte von Variablen abliest. Wie in jedem C-Programm beginnt die Programmausführung damit, dass zuerst die globalen Variablen erzeugt werden. char buffer[10]; uint8_t position; volatile uint8_t habekommando; Also machst du das mal auf deinem Papier buffer +---+---+---+---+---+---+---+---+---+---+ | | | | | | | | | | | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | | | | +-----+ +------+ Bei globalen Variablen gibt es noch einen Sonderfall: Sie werden automatisch mit 0 initialisiert. Also mach das mal buffer +---+---+---+---+---+---+---+---+---+---+ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 0 | | 0 | +-----+ +------+ Nachdem die Initialsierug abgeschlossen ist, beginnt die Programmausführung bei main(). Dort wird die USART initialisiert, die Interrupts freigegeben und danach die while Schleife betreten. In der while Schleife wird ständig habekommando auf 1 abgefragt. Dein Papier (=Speicher) zeigt, das habekommando den Wert 0 hat, daher passiert jetzt noch nichts weiter in der while Schleife. Aber jetzt nehmen wir mal an, dass ein Zeichen über die USART herein kommt. Konkret nehmen wir mal an, das wäre ein 'C' gewesen. Als Folge davon wird die ISR aufgerufen. Was macht die? char c = UDR; Da wird also eine neue Variable c erzeugt. Also machen wir das mal: buffer +---+---+---+---+---+---+---+---+---+---+ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 0 | | 0 | +-----+ +------+ c +-----+ | | +-----+ Aber damit nicht genug. Die Initialiserung von c sagt, dass sie den Wert von UDR bekommt. UDR steht für das Register in dem die UART das empfangene Zeichen abgelegt hat. Da kommt also das empfangene 'C' wieder heraus und wird in der Variablen c abgelegt: buffer +---+---+---+---+---+---+---+---+---+---+ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 0 | | 0 | +-----+ +------+ c +-----+ | C | +-----+ Was kommt als nächstes: if (c == 'E') Sieh am Papier nach (das ist wichtig: Immer im Speicher nachsehen, welchen Wert Variablen tatsächlich haben und sich nicht darauf verlasssen, das man deren Wert schon wüsste). c hat den Wert 'C' und das ist ungleich 'E'. Also wird nicht der then Zweig genommen sondern der else Zweig. Was passier dort? buffer[position] = c; Welchen Wert hat position? Ein Blick auf das Papier verrät uns: 0 Also wird an buffer[0] der Wert von c zugewiesen. c hatte den Wert 'C', also sieht das Ergebnis so aus: buffer +---+---+---+---+---+---+---+---+---+---+ | C | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 0 | | 0 | +-----+ +------+ c +-----+ | C | +-----+ Was passiert weiter? position++; Die Variable position wird um 1 erhöht buffer +---+---+---+---+---+---+---+---+---+---+ | C | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 1 | | 0 | +-----+ +------+ c +-----+ | C | +-----+ Nachdem auch diese Anweisung ausgeführt wurde, gibt es nichts weiter in der Funktion zu tun und die Funktion wird beendet. Wie bei jeder anderen Funktion ist damit noch eine automatische Aktion verknüpft: die lokalen Variablen werden zerstört. In diesem Fall handelt es sich um die Variable c. Also: Radiergummi raus und c ausradieren: buffer +---+---+---+---+---+---+---+---+---+---+ | C | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 1 | | 0 | +-----+ +------+ Das ist also die Situation der globalen Variablen nach dem Empfang des ersten Zeichens. Lass uns noch ein Zeichen empfangen: ein 'P'. Wieder beginnt die ISR mit dem Erzeugen von c und dem holen des Zeichens nach c buffer +---+---+---+---+---+---+---+---+---+---+ | C | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 1 | | 0 | +-----+ +------+ c +-----+ | P | +-----+ if (c == 'E') Ein Blick aufs Papier verrät, dass c kein 'E' enthält, also kommt wieder der else Zweig zum Zug: buffer[position] = c; position (am Papier nachsehen) enthält den Wert 1, also wird an buffer[1] der Inhalt von c zugewiesen buffer +---+---+---+---+---+---+---+---+---+---+ | C | P | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 1 | | 0 | +-----+ +------+ c +-----+ | P | +-----+ position++; Auch das ist wieder leicht: position um 1 erhöhen: buffer +---+---+---+---+---+---+---+---+---+---+ | C | P | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 2 | | 0 | +-----+ +------+ c +-----+ | P | +-----+ Die Funktion ist wieder fertig, die lokalen Variablen werden zerstört: buffer +---+---+---+---+---+---+---+---+---+---+ | C | P | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 2 | | 0 | +-----+ +------+ Zu guter letzt, lasen wir noch ein Zeichen empfangen: diesesmal ein 'E'. Die ISR beginnt: char c = UDR; buffer +---+---+---+---+---+---+---+---+---+---+ | C | P | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 2 | | 0 | +-----+ +------+ c +-----+ | E | +-----+ if (c == 'E') Ein Blick auf c verrät, diesmal enthält c tatsächlich ein 'E'. Also wird der then Zweig genommen: buffer[position] = '\0'; buffer +---+---+---+---+---+---+---+---+---+---+ | C | P | \0| 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 2 | | 0 | +-----+ +------+ c +-----+ | E | +-----+ (Damit ist in Buffer ein gültiger String entstanden: Eine Folge von Zeichen die mit '\0' abgeschlossen ist) habekommando = 1; buffer +---+---+---+---+---+---+---+---+---+---+ | C | P | \0| 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 2 | | 1 | +-----+ +------+ c +-----+ | E | +-----+ position = 0; buffer +---+---+---+---+---+---+---+---+---+---+ | C | P | \0| 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 0 | | 1 | +-----+ +------+ c +-----+ | E | +-----+ Das position hier wieder auf 0 gesetzt wird, sollte nach dem ganzen Vorgeplänkel einsichtig sein. position wird ja benutzt um zu entscheiden wo im Buffer das nächste empfangene Zeichen abgelegt wird. Nachdem also dieser empfangene String abgearbeitet wurde, wird das nächste empfangene Zeichen wieder in buffer[0] abgelegt. Und dafür muss position den Wert 0 haben. led_init(UDR); Dieser Aufruf hat hier eigentlich nichts zu suchen. Er gehört logisch nicht hier her. Aber was solls. führen wir ihn aus: Das Argument ist UDR. UDR ist auch hier wieder das Register in dem Zeichen von der USART empfangen werden. Nur: Bei jedem lesenden zugriff auf UDR wird dieses register gelöscht. Die Verwendung von UDR an dieser Stelle liefert also 0 Die Funktion wird aufgerufen. In der Funktion led_init gibt es keine lokalen Variablen, also braucht auch auf dem Papier keine erzeugt werden. if(strcmp (buffer, "CP001201E") == 0) Hier wird ein Stringvergleich von buffer mit dem String "CP001201E" gemacht. Ein Blick auf das Papier enthüllt: buffer +---+---+---+---+---+---+---+---+---+---+ | C | P | \0| 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 0 | | 1 | +-----+ +------+ c +-----+ | E | +-----+ buffer enthält den String "CP". Da dieser String nicht gleich "CP001201E" ist, liefert strcmp keine 0. Der else Zweig kommt zum Zug. else if(strcmp (buffer, "CP001200E") == 0) { buffer ist auch nicht gleich "CP001200E", also kommt auch hier der else Zweig zum Zug, den es aber nicht mehr gibt. Damit ist die Funktion zu Ende und es geht wieder zurück zum Aufrufer, in diesem Falle in die ISR. In der ISR passiert aber nichts mehr nach dem Funktionsaufruf. Die ISR beendet sich (und löst die globalen Variablen auf) buffer +---+---+---+---+---+---+---+---+---+---+ | C | P | \0| 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 0 | | 1 | +-----+ +------+ Nachdem die ISR abgearbeitet wurde, geht die Programmausführung ganz normal in main() weiter, in der while Schleife, die ja durch die ISR unterbrochen wurde. In der while Schleife wird habekommando auf 1 überprüft, und das ist jetzt auch der Fall! Warum? Weil die ISR erkannt hat, dass ein Kommando vollständig empfangen wurde und daher habekommando auf 1 gesetzt hat. if (habekommando == 1) Hier wird also das then ausgeführt. Und eigentlich sollte hier der Aufruf von led_init stattfinden. Denn hier ist die Stelle an der bekannt ist, dass ein Kommando empfangen wurde und an der die Abarbeitung des Kommandos hingehört. Anschliessend wird habekommando wieder auf 0 gesetzt: habekommando = 0; buffer +---+---+---+---+---+---+---+---+---+---+ | C | P | \0| 0 | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ position habekommando +-----+ +------+ | 0 | | 0 | +-----+ +------+ Und damit sind wir wieder fast in der Ausgangsposition. habekommando zeigt an, dass es kein empfangens, nicht abgearbeitetes Kommando gibt, und position hat korrekter- weise wieder den Wert 0. Das nächste empfangene Zeichen wird also wieder bei buffer[0] abgelegt, position um 1 erhöht etc. bis ein empfangenes 'E' anzeigt, dass das übertragene Kommando vollständig ist. In diesem Fall wird die ISR in buffer wieder einen kompletten String aufgebaut haben und habekommando auf 1 setzen. Die while Schleife in main() sieht diese 1, bearbeitet das Kommando und setzt habekommando wieder auf 0 zurück. etc. etc. etc. Du siehst also: Mit Papier und Bleistift ist es gar nicht so schwer herauszufinden, was eine Funktion macht und Einsichten zu gewinnen, warum es genau dieses tut.
Danke für deinen ausführlichen Beitrag. Er hat mir sehr weitergeholfen!
Hallo, ich habe das Programm noch geändert, so wie sie es beschrieben hatten, doch beim senden der String_Kette CP001200 oder CP001201 passiert níchts. ich glaube es liegt an der ISR, die springt einfach nicht an. Ich habe nur in der main() led_init() eingesetzt und aus dem obigen Beispiel rausgenommen. Können Sie mir bitte weiterhelfen! int main(void) { DDRA = 0xff; uart_init(); sei(); while(1) { if (habekommando == 1) { led init(); habekommando = 0; } } return 0; }
Christoph G. wrote: > ich habe das Programm noch geändert, so wie sie es beschrieben hatten, > doch beim senden der String_Kette CP001200 oder CP001201 passiert > níchts. Der allererste Schritt sollte immer seibn zunächst mal zu überprüfen, ob am µC auch tatsächlich die Zeichenkette ankommt, von der du glaubst, dass sie ankommt: Dazu schickt man zb. einfach mal die Zeichenkette wieder zurück, um sie auf einem Terminal ausgeben und begutachten zu können. > ich glaube es liegt an der ISR, die springt einfach nicht an. Woher weist du das? Hast du das getestet? Ein einfacher Test wäre es zb. in der ISR einfach mal eine LED einschalten zu lassen. Geht die LED an, wird die ISR ausgeführt. Geht sie nicht an, wird die ISR nicht ausgeführt. Damit hast du schon mal das 'ich glaube, dass ...' in ein 'ich weiss, dass ...' überführt.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.