Forum: Mikrocontroller und Digitale Elektronik Zeichenkette erstellen


von avr-gcc (Gast)


Lesenswert?

1
   uart_putc(27);
2
   uart_puts("[2J");
3
   uart_putc(27);
4
   uart_puts("[H");

Wie kann man sowas als einen befehl schreiben?

von Karl H. (kbuchegg)


Lesenswert?

1
  uart_puts("\x1B[2J\x1B[H"):

Escape Sequenzen in Strings sollten sich eigentlich in jedem C-Lehrbuch 
finden. Wenn nicht: schmeiss es weg und kauf dir was ordentliches

von Rolf M. (rmagnus)


Lesenswert?

avr-gcc schrieb:
> uart_putc(27);
>    uart_puts("[2J");
>    uart_putc(27);
>    uart_puts("[H");
>
> Wie kann man sowas als einen befehl schreiben?

Ich würd's so machen:
1
#define ESC "\033"
2
3
uart_puts(ESC "[2J" ESC "[H");

Oder noch etwas weiter:
1
#define ESC    "\033"
2
#define CSI    ESC "["
3
#define ED     CSI "2J"
4
#define HOME   CSI "H"
5
6
7
uart_puts(ED HOME);

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Rolf M. schrieb:
> #define ESC "\033"

Oktal ist natürlich obercool.

von avr-gcc (Gast)


Lesenswert?

Habe folgende Befehle gefunden
1
#define  ANSI_ESCAPE_SEQUENCE(c)        "\33["c
2
#define  ESC_RESET        ANSI_ESCAPE_SEQUENCE("0m")
3
#define  ESC_BOLD_ON        ANSI_ESCAPE_SEQUENCE("1m")
4
#define  ESC_ITALICS_ON      ANSI_ESCAPE_SEQUENCE("3m")
5
#define  ESC_UNDERLINE_ON    ANSI_ESCAPE_SEQUENCE("4m")
6
#define  ESC_INVERSE_ON      ANSI_ESCAPE_SEQUENCE("7m")
7
#define  ESC_STRIKETHROUGH_ON  ANSI_ESCAPE_SEQUENCE("9m")
8
#define  ESC_BOLD_OFF      ANSI_ESCAPE_SEQUENCE("22m")
9
#define  ESC_ITALICS_OFF      ANSI_ESCAPE_SEQUENCE("23m")
10
#define  ESC_UNDERLINE_OFF    ANSI_ESCAPE_SEQUENCE("24m")
11
#define  ESC_INVERSE_OFF      ANSI_ESCAPE_SEQUENCE("27m")
12
#define  ESC_STRIKETHROUGH_OFF  ANSI_ESCAPE_SEQUENCE("29m")
13
#define  ESC_FG_BLACK      ANSI_ESCAPE_SEQUENCE("30m")
14
#define  ESC_FG_RED        ANSI_ESCAPE_SEQUENCE("31m")
15
#define  ESC_FG_GREEN      ANSI_ESCAPE_SEQUENCE("32m")
16
#define  ESC_FG_YELLOW      ANSI_ESCAPE_SEQUENCE("33m")
17
#define  ESC_FG_BLUE        ANSI_ESCAPE_SEQUENCE("34m")
18
#define  ESC_FG_MAGENTA      ANSI_ESCAPE_SEQUENCE("35m")
19
#define  ESC_FG_CYAN        ANSI_ESCAPE_SEQUENCE("36m")
20
#define  ESC_FG_WHITE      ANSI_ESCAPE_SEQUENCE("37m")
21
#define  ESC_FG_DEFAULT      ANSI_ESCAPE_SEQUENCE("39m")
22
#define  ESC_BG_BLACK      ANSI_ESCAPE_SEQUENCE("40m")
23
#define  ESC_BG_RED        ANSI_ESCAPE_SEQUENCE("41m")
24
#define  ESC_BG_GREEN      ANSI_ESCAPE_SEQUENCE("42m")
25
#define  ESC_BG_YELLOW      ANSI_ESCAPE_SEQUENCE("43m")
26
#define  ESC_BG_BLUE        ANSI_ESCAPE_SEQUENCE("44m")
27
#define  ESC_BG_MAGENTA      ANSI_ESCAPE_SEQUENCE("45m")
28
#define  ESC_BG_CYAN        ANSI_ESCAPE_SEQUENCE("46m")
29
#define  ESC_BG_WHITE      ANSI_ESCAPE_SEQUENCE("47m")
30
#define  ESC_BG_DEFAULT      ANSI_ESCAPE_SEQUENCE("49m")
31
#define  ESC_CURSOR_POS(L,C)    ANSI_ESCAPE_SEQUENCE(#L ";" #C "H")
32
#define  ESC_CURSOR_UP(L)    ANSI_ESCAPE_SEQUENCE(#L "A")
33
#define  ESC_CURSOR_DOWN(L)    ANSI_ESCAPE_SEQUENCE(#L "B")
34
#define  ESC_CURSOR_FORWARD(C)  ANSI_ESCAPE_SEQUENCE(#C "C")
35
#define  ESC_CURSOR_BACKWARD(C)  ANSI_ESCAPE_SEQUENCE(#C "D")
36
#define  ESC_CURSOR_POS_SAVE    ANSI_ESCAPE_SEQUENCE("s")
37
#define  ESC_CURSOR_POS_RESTORE  ANSI_ESCAPE_SEQUENCE("u")
38
#define  ESC_ERASE_DISPLAY    ANSI_ESCAPE_SEQUENCE("2J")
39
#define  ESC_ERASE_LINE      ANSI_ESCAPE_SEQUENCE("K")

Welche ich wie folgt einlesen möchte
1
void uart_command(const char *s)
2
{
3
  uart_puts(s);
4
}

Das ganze funktioniert auch recht gut. Nun zwei Fragen,
wenn ich das Display löschen möchte,
wie sieht der Befehl von oben nun hier aus?
1
 void uart_clear(void)
2
 {
3
   lcd_command(ESC_ERASE_DISPLAY);
4
   lcd_command(ESC_CURSOR_POS_RESTORE); // ????
5
 }

Des weiteren habe ich keine Möglichkeit, den Cursor zu setzen. Das 
sollte in Putty soweit bekannt eigentlich gehen. Attribute wie 
ESC_BG_RED funktionieren alle.
1
void uart_setCursor(uint8_t x, uint8_t y)
2
{
3
  uart_command(ESC_CURSOR_POS(x, y));
4
}

von Karl H. (kbuchegg)


Lesenswert?

Frage 1:
Du gibst dir doch die Antwort schon selbst. Wenn du das Display nur 
löschen möchtest, dann mach auch genau das. Was genau hat 
CURSOR_RESTORE_POS mit dem Löschen des Displays zu tun. Du brauchst den 
Makro Namen nur zu lesen um zu wissen, dass da irgendwas mit dem Cursor 
passiert und das hat erst mal nichts damit zu tun, dass die Anzeige 
geleert wir

von Rolf M. (rmagnus)


Lesenswert?

avr-gcc schrieb:
> void uart_setCursor(uint8_t x, uint8_t y)
> {
>   uart_command(ESC_CURSOR_POS(x, y));
> }

Wie sollte das denn auch gehen? Die Markos erstellen jeweils ein "string 
literal" also eine Zeichenkettenkonstante. Du übergibst hier aber 
Variablen. Wenn du die brauchst, mußt du den String zur Laufzeit 
zusammenbasteln. Das können diese Makros nicht leisten.
Das Makro nimmt einfach x und y als Text und fügt das ein, es ensteht 
also
1
"\33[x;yH"
und daß dein Terminal mit diesem String nix anfangen kann, ist klar.

von Karl H. (kbuchegg)


Lesenswert?

Zur Frage 2:
Das kann auch nicht funktionieren, wenn dir klar waere, dass Makros im 
Grunde nur Textersetzungen sind, die zur Compilier Zeit vorgenommen 
werden.
Dein Makro expandiert also im besten Fall zu einem String, in dem  die 
Buchstaben x und y vorkommen und nicht die entsprechenden Zahlenwerte

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

avr-gcc schrieb:
> wie sieht der Befehl von oben nun hier aus? void uart_clear(void)
>  {
>    lcd_command(ESC_ERASE_DISPLAY);
>    lcd_command(ESC_CURSOR_POS_RESTORE); // ????
>  }

Nein. Um eine Cursor-Position zu restoren, mußt du erstmal eine 
gespeichert haben. Dein ESC [H gibt es in deinen Makros nicht. Das habe 
ich auch erstmal eine Weile suchen müssen, weil es offenbar keine 
Standard-ANSI-Escape-Sequenz ist. Da kommen vor dem H normalerweise noch 
die Zielkoordinaten (Siehe ESC_CURSOR_POS).
Übrigens kannst du die Kommandos auch mit einem Aufruf schicken:
1
    lcd_command(ESC_ERASE_DISPLAY ESC_CURSOR_POS_RESTORE);

: Bearbeitet durch User
von Noch einer (Gast)


Lesenswert?

> Oktal ist natürlich obercool.

Ach was, damit hat Rolf auf dem International Obfuscated C Code Contest 
keine Chance.

Problem ist, einen sinnvollen Mittelweg zu finden.

Nimmt man "\033[2J", weiß der nächste nicht mehr, was 2J eigentlich 
macht. Nimmt man ein geniales System an Defines, weiß der nächste nicht 
mehr, wie man des Programm auf ein anderes Terminal umstellt.

Mit den gefundenen Befehlen hast du dir halt 2 Probleme eingehandelt. Du 
musst herausfinden, was Putty erwartet. Und zusätzlich musst du 
herausfinden, ob diese Defines das richtige liefern.

Wenn beides zusammen zu verwirrend ist, warum bleibst du dann nicht 
einfach bei dem Vorschlag von Karl Heinz? Oder einfach bei den 4 Zeilen, 
mit denen du es ursprünglich gelöst hast?

von avr-gcc (Gast)


Lesenswert?

1
void uart_clear(void)
2
{
3
  uart_command(ESC_ERASE_DISPLAY);
4
  uart_command(ESC_CURSOR_POS(0,0));
5
}
Geht so.

Nur bin ich noch nicht weiter, wie ich das mit dem setCursor realisieren 
soll. Konstante Werte gehen ja.
1
uart_command(ESC_CURSOR_POS(5,5));

Nur sowas leider nicht
1
    _delay_ms(200);
2
    int y= 0;
3
    uart_command(ESC_CURSOR_POS(0,y));
4
    uart_putc('a');
5
    y++;

von avr-gcc (Gast)


Lesenswert?

1
void uart_setcursor(uint8_t x, uint8_t y)
2
{
3
  char buffer[20];
4
  char buffer2[10];
5
  strcat(buffer, '\33');
6
  itoa(x, buffer2, 10);
7
  strcat(buffer, buffer2);
8
  itoa(y, buffer2, 10);
9
  strcat(buffer, buffer);
10
  strcat(buffer, y+'0');
11
  strcat(buffer, 'H');
12
13
  uart_command(buffer);
14
}

Bringt meinen µC leider in einem undefinierten Zustand -> stürzt ab.

von Karl H. (kbuchegg)


Lesenswert?

avr-gcc schrieb:
>
1
> void uart_clear(void)
2
> {
3
>   uart_command(ESC_ERASE_DISPLAY);
4
>   uart_command(ESC_CURSOR_POS(0,0));
5
> }
> Geht so.
>
> Nur bin ich noch nicht weiter, wie ich das mit dem setCursor realisieren
> soll. Konstante Werte gehen ja.
>
>
1
> uart_command(ESC_CURSOR_POS(5,5));
2
>
>
> Nur sowas leider nicht
>
1
>     _delay_ms(200);
2
>     int y= 0;
3
>     uart_command(ESC_CURSOR_POS(0,y));
4
>     uart_putc('a');
5
>     y++;

Wie kriegt man denn zur Laufzeit einen String, bei dem Zahlenwerte aus 
dem laufenden Programm eingesetzt werden?

Sorry. Aber ein bisschen Nachdenken musst du schon auch selbst. Vergiss 
mal die Makros

von avr-gcc (Gast)


Lesenswert?

Hallo kh,

habe es jetzt nun mal Testweise mit sprintf versucht. Geht, aber erzeugt 
auf dem Attiny einen ordentlichen Zuwachs des Programm Speichers. (+10%)

Ich würde gerne die Funktion (sprintf) aus diesem Grund vermeiden, da 
sie nicht wirklich andersweitig benötigt wird.

void uart_setcursor(uint8_t x, uint8_t y)
{
  char str[20];
  sprintf(str,"\33[%d;%dH",y,x);

  uart_command(str);
}

von Rolf M. (rmagnus)


Lesenswert?

avr-gcc schrieb:
> Bringt meinen µC leider in einem undefinierten Zustand -> stürzt ab.

Da sind auch ziemlich viele Fehler drin.

> void uart_setcursor(uint8_t x, uint8_t y)
> {
>   char buffer[20];
>   char buffer2[10];
>   strcat(buffer, '\33');

Das strcat sucht im uninitialisierten Puffer nach dem ersten \0. 
Außerdem erwartet es die Adresse eines Strings. '\33' ist keine Adresse.

>   itoa(x, buffer2, 10);
>   strcat(buffer, buffer2);
>   itoa(y, buffer2, 10);
>   strcat(buffer, buffer);

Warum wird da der Buffer an sich selbst gehängt?

>   strcat(buffer, y+'0');
>   strcat(buffer, 'H');

Dei beiden haben das gleiche Problem wie oben. Und wo ist das Semikolon 
zwischen den beiden Zahlen? Warum wird y zweimal angehängt?

>   uart_command(buffer);
> }

von Karl H. (kbuchegg)


Lesenswert?

avr-gcc schrieb:
> Hallo kh,
>
> habe es jetzt nun mal Testweise mit sprintf versucht. Geht, aber erzeugt
> auf dem Attiny einen ordentlichen Zuwachs des Programm Speichers. (+10%)

ok. Dann musst du dir einen gleichwertigen Ersatz dafür sorgen, der 
genau das kann, was du erreichen willst.

Hinweis: dem Terminal ist es völig egal, ob du in Etappen ausgibst, oder 
ob du dir die Mühe machst, den String erst mal komplett zusammen zu 
setzen. Das Terminal sieht nur die Zeichen, die aus der Schnittstelle 
rauskommen. Und das geht in jedem Fall langsamer, als ein paar 
Funktionsaufrufe für Subfunktionen, die zb einen String oder eine Zahl 
per UART auf die Reise schickt.

Du willst also
* eine textuelle 'Eröffnungsseqqquenz' senden
* dann die Zeile (in ihrer gewandelten Form als Text)
* dann einen ,
* dann die Spalte (in ihrer gewandelten Form als Text)
* die Abschlusssequenz, in der dann ausgesagt wird, welches Kommando das 
ist


Niemand sagt, dass du alles erst mal in einen String sammeln musst. Du 
kannst es auch auf Etappen machen und einzeln auf die Reise schicken.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Karl H. schrieb:

Und noch ein Hinweis. Hier ...
> * dann die Zeile (in ihrer gewandelten Form als Text)
... verbirgt sich eine weitere Hilfsfunktion drinnen, die man praktisch 
ständig immer wieder braucht: Nämlich die Ausgabe einer Zahl in lesbarer 
Form über die UART. Es wird also sinnvoll sein, ähnlich wie bei der 
Hilfsfunktion die einen String ausgeben kann, sich hier eine 
Hilfsfunktion zu machen die eben genau das kann: Zahlen in ihrer 
textuellen Form über die UART auszugeben.

von avr-gcc (Gast)


Lesenswert?

Meinst du das so?
1
void uart_setcursor(uint8_t x, uint8_t y)
2
{
3
  /*char str[20];
4
  sprintf(str,"\33[%d;%dH",y,x);
5
  uart_command(str);*/
6
  char buffer[4];
7
  uart_command("\33[");
8
  uart_command(itoa(y, buffer, 10));
9
  uart_command(";");
10
  uart_command(itoa(x, buffer, 10));
11
  uart_command("H");
12
}

Funktioniert zumindest bedingt schon einmal.
Er macht einmal den Zeilensprung in die 2. Zeile. Danach schreibt er 
aber alle Werte immer in die erste Zeile. Die Spalten werden richtig 
gesetzt.

1
int main(void)
2
{
3
  uart_init();
4
  lcd_init();
5
  _delay_ms(500);
6
  sei();
7
  
8
  uart_clear();
9
  uart_puts_P("      Test      ");
10
  uart_setcursor(0,2);
11
  uart_puts_P(" ATtiny841 INIT ");
12
  
13
  lcd_clear();
14
  lcd_puts("      Test      ");
15
  lcd_setcursor(0,1);
16
  lcd_puts(" ATTiny841 INIT ");
17
  _delay_ms(2000);
18
  
19
  uart_clear();
20
  lcd_clear();
21
  
22
  while(1)
23
  {
24
    const int16_t val[] = {-16000, -9999, - 999, -99, -9, 0, 9, 99, 999, 9999, 16000};
25
    static int16_t count;
26
    uart_setcursor(5,0);
27
    uart_puts_P("      ");
28
    uart_setcursor(5,0);
29
    uart_puti(val[count]);
30
    uart_setcursor(7, 1);
31
    uart_puti_format(val[count], 5);
32
    
33
    lcd_setcursor(5,0);
34
    lcd_puts("      ");
35
    lcd_setcursor(5,0);
36
    lcd_puti(val[count]);
37
    lcd_setcursor(7, 1);
38
    lcd_puti_format(val[count], 5);
39
    
40
    count = (count + 1) % 11;
41
    _delay_ms(500);
42
  }
43
}

von Karl H. (kbuchegg)


Lesenswert?

avr-gcc schrieb:
> Meinst du das so?

IM Prinzip: ja.

Nur wie schon gesagt: Dia Ausgabe einer Zahl ist etwas, was man immer 
wieder mal braucht. D.h. man baut sich da eine Hilfsfunktion
1
void uart_puti( int zahl )
2
{
3
  char str[7];
4
5
  itoa( zahl, str, 10 );
6
  uart_command( zahl );
7
}

btw. uart_command ist ein schlechter Name. Denn tatsächlich tut die 
Funktion nichts anderes als einen String auszugeben. Dass dieser String 
ein Kommando ist, braucht hier nicht mehr zu interessieren.

Damit reduziert sich deine Funktion zu
1
void uart_setcursor(uint8_t x, uint8_t y)
2
{
3
  uart_command("\33[");
4
  uart_puti( y );
5
  uart_command(";");
6
  uart_puti( x );
7
  uart_command("H");
8
}

und das sieht dann doch gar nicht mehr so schlecht aus.


Ooops

>     uart_puts_P("      ");
>     uart_setcursor(5,0);
>     uart_puti(val[count]);

du hast die Funktion bereits!
Warum verwendest du sie dann nicht?

von Karl H. (kbuchegg)


Lesenswert?

avr-gcc schrieb:

> Funktioniert zumindest bedingt schon einmal.
> Er macht einmal den Zeilensprung in die 2. Zeile. Danach schreibt er
> aber alle Werte immer in die erste Zeile. Die Spalten werden richtig
> gesetzt.

Na ja. Was erwartest du. Sieh dir mal deine Aufrufe an.
Du schickst den Cursor immer an dieselben Positionen.

von avr-gcc (Gast)


Lesenswert?

Hallo Karl Heinz,

verstehe das nicht, dass ich immer in der gleichen Zeile schreibe.

Im ersten Teil setze ich den Cursor auf Spalte 5, Zeile 0.
Der alte Text wird ersetzt durch Leerzeichen.
Danach wird der Cursor wieder auf Spalte 5, Zeile 0 gesetzt, damit dort 
der neue Wert überschrieben werden kann.

Danach soll in Spalte 7, Zeile 1 gewechselt werden. Dort erfolgt eine 
formatierte Ausgabe. Löschen der alten Zeichen nicht erforderlich, da es 
die Funktion macht.
1
    uart_setcursor(5,0);
2
    uart_puts_P("      ");
3
4
    uart_setcursor(5,0);
5
    uart_puti(val[count]);
6
7
    uart_setcursor(7, 1);
8
    uart_puti_format(val[count], 5);

von Karl H. (kbuchegg)


Lesenswert?

avr-gcc schrieb:
> Hallo Karl Heinz,
>
> verstehe das nicht, dass ich immer in der gleichen Zeile schreibe.

Was verstehst du daran nicht.
Wenn du uart_setcursor mit immer den gleichen Zahlenwerten aufrufst, 
dann setzt dir die Funktion den Cursor auch immer in genau die 
Zeile/Spalte, die du angegeben hast.

> Danach soll in Spalte 7, Zeile 1 gewechselt werden. Dort erfolgt eine
> formatierte Ausgabe. Löschen der alten Zeichen nicht erforderlich, da es
> die Funktion macht.

Ja schön.
Danach ist deine Schleife einmal durch und das Spielchen beginnt von 
vorne: Cursor nach 5,0, ausgeben, etc.

Wenn ich deine Frage richtig verstanden habe, dann ist das auch genau 
das, was du siehst. Also passiert genau das, was du programmiert hast.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

avr-gcc schrieb:

> Danach soll in Spalte 7, Zeile 1 gewechselt werden. Dort erfolgt eine
> formatierte Ausgabe. Löschen der alten Zeichen nicht erforderlich, da es
> die Funktion macht.

OK. Und wie macht das die Funktion?

von avr-gcc (Gast)


Lesenswert?

Hallo,

also nach meiner Überlegung dürfte es so nicht aussehen.

Erwartet wird hier eine Ausgabe wie folgt. ('.' = ' ')
1
1)
2
.....abcdef..... 
3
.......abcdef...
4
5
2)
6
uart_setcursor(5,0);
7
uart_puts_P("      ");
8
................ (alter Wert wird mit Leerzeichen überschrieben)
9
.......abcdef... (noch unverändert)
10
11
3)
12
uart_setcursor(5,0);
13
uart_puti(val[count]);
14
.....uvwxyz.....  (neuer Wert wird geschrieben)
15
.......abcdef... (noch unverändert)
16
17
4)
18
uart_setcursor(7, 1);
19
uart_puti_format(val[count], 5);
20
.....uvwxyz.....
21
.......uvwxyz... (neuer Wert wird geschrieben)

von avr-gcc (Gast)


Lesenswert?

1
void uart_setcursor(uint8_t x, uint8_t y)
2
{
3
  uart_puts("\33[");
4
  uart_puti(y);
5
  uart_puts(";");
6
  uart_puti(x);
7
  uart_puts("H");
8
}

von Karl H. (kbuchegg)


Lesenswert?

avr-gcc schrieb:
> Hallo,
>
> also nach meiner Überlegung dürfte es so nicht aussehen.

Das Problem ist, dass hier keine sieht oder weiss, wie es bei dir am 
Terminal *tatsächlich aussieht.

Beschreib es nicht, sondern mach einen Screenshot

von avr-gcc (Gast)


Angehängte Dateien:

Lesenswert?

Bild 1:
Wird so ausgeführt, wie erhofft.

Bild 2 wird nicht so ausgeführt wie erhofft, es findet kein Sprung in 
die angegeben Zeile statt.

von Karl H. (kbuchegg)


Lesenswert?

Mach da
>   uart_puts("\33[");
mal
1
  uart_puts("\033[");
draus. Theoretisch sollte es keinen Unterschied machen. Aber sicher ist 
sicher

von Karl H. (kbuchegg)


Lesenswert?

Hast du schon kontrolliert, ob die enstprechende Emulation in deinem 
Terminal Programm auch aktiviert ist?

Alternativ könntest du auch einfach mal den \33 Teil weglassen und dir 
am Terminal ansehen, was du tatsächlich innerhalb der Escape Sequenz 
schickst (wenn dein Terminal keine Möglichkeit hat, dir das in Rohform 
anzuzeigen)

von Karl H. (kbuchegg)


Lesenswert?

Ich bin mir ausserdem jezt gar nicht mehr sicher, mit welcher Zahlen bei 
der Cursorpositionierung begonnen wird zu zählen. Ist schon zu lange 
her.

Ist die oberste Zeile die Zeile 0 oder die Zeile 1?

Einfach ausprobieren. Schicke den Cursor mal in die Zeile 2
1
  uart_setcursor(7, 2);

von avr-gcc (Gast)


Lesenswert?

Mit 1. Nicht 0.

Das war der Fehler

von Karl H. (kbuchegg)


Lesenswert?

avr-gcc schrieb:
> Mit 1. Nicht 0.
>
> Das war der Fehler

Manchmal hat man aber auch Tomaten auf den Augen :-)

von Rolf M. (rmagnus)


Lesenswert?

Ist ja auch fies. Kein normal denkender Programmierer fängt bei der 1 
an. ;-)

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.