Forum: Compiler & IDEs Kaputt optimiert - was mache ich falsch?


von Rudolph R. (rudolph)


Lesenswert?

Hi!

Ich habe den folgenden Code:
1
uint8_t display_busy(void)
2
{
3
 uint8_t x;
4
5
 DDRA = 0x00;      // set all Pins from PORTA to input
6
 PORTB |=  LCD_RW; // switch display to read-mode
7
 PORTB &= ~LCD_RS; // clear RS, this is always a command
8
 PORTB |=  LCD_E;  // set E
9
 x = PINA & 0x08;  // we only need the busy-flag
10
 PORTB &= ~LCD_E;  // clear E
11
 // read second nibble to complete access
12
 PORTB |=  LCD_E;  // set E
13
 PORTB &= ~LCD_E;  // clear E
14
 return(x);
15
}

Ist die Busy-Flag Abfrage für ein Display mit 4-Bit Interface.

Das Problem ist jetzt, das funktioniert nur mit "-O0".
Sobald ich "-O1" oder höher setze wird das kaputt-optimiert.

Der Code bei "-O0" (leicht gekürzt am Ende):
1
uint8_t display_busy(void)
2
{
3
 13c:  cf 93         push  r28
4
 13e:  df 93         push  r29
5
 140:  cd b7         in  r28, 0x3d  ; 61
6
 142:  de b7         in  r29, 0x3e  ; 62
7
 144:  21 97         sbiw  r28, 0x01  ; 1
8
 146:  0f b6         in  r0, 0x3f  ; 63
9
 148:  f8 94         cli
10
 14a:  de bf         out  0x3e, r29  ; 62
11
 14c:  0f be         out  0x3f, r0  ; 63
12
 14e:  cd bf         out  0x3d, r28  ; 61
13
 uint8_t x;
14
15
 DDRA = 0x00;      // set all Pins from PORTA to input
16
 150:  ea e3         ldi  r30, 0x3A  ; 58
17
 152:  f0 e0         ldi  r31, 0x00  ; 0
18
 154:  10 82         st  Z, r1
19
 PORTB |=  LCD_RW; // switch display to read-mode
20
 156:  a8 e3         ldi  r26, 0x38  ; 56
21
 158:  b0 e0         ldi  r27, 0x00  ; 0
22
 15a:  e8 e3         ldi  r30, 0x38  ; 56
23
 15c:  f0 e0         ldi  r31, 0x00  ; 0
24
 15e:  80 81         ld  r24, Z
25
 160:  84 60         ori  r24, 0x04  ; 4
26
 162:  8c 93         st  X, r24
27
 PORTB &= ~LCD_RS; // clear RS, this is always a command
28
 164:  a8 e3         ldi  r26, 0x38  ; 56
29
 166:  b0 e0         ldi  r27, 0x00  ; 0
30
 168:  e8 e3         ldi  r30, 0x38  ; 56
31
 16a:  f0 e0         ldi  r31, 0x00  ; 0
32
 16c:  80 81         ld  r24, Z
33
 16e:  87 7f         andi  r24, 0xF7  ; 247
34
 170:  8c 93         st  X, r24
35
 PORTB |=  LCD_E;  // set E
36
 172:  a8 e3         ldi  r26, 0x38  ; 56
37
 174:  b0 e0         ldi  r27, 0x00  ; 0
38
 176:  e8 e3         ldi  r30, 0x38  ; 56
39
 178:  f0 e0         ldi  r31, 0x00  ; 0
40
 17a:  80 81         ld  r24, Z
41
 17c:  82 60         ori  r24, 0x02  ; 2
42
 17e:  8c 93         st  X, r24
43
 x = PINA & 0x08;  // we only need the busy-flag
44
 180:  e9 e3         ldi  r30, 0x39  ; 57
45
 182:  f0 e0         ldi  r31, 0x00  ; 0
46
 184:  80 81         ld  r24, Z
47
 186:  88 70         andi  r24, 0x08  ; 8
48
 188:  89 83         std  Y+1, r24  ; 0x01
49
 PORTB &= ~LCD_E;  // clear E
50
 ...
51
 1c8:  08 95         ret

Der gleiche Code bei "-O1":
1
uint8_t display_busy(void)
2
{
3
  e6:  1a ba         out  0x1a, r1  ; 26
4
 uint8_t x;
5
6
 DDRA = 0x00;      // set all Pins from PORTA to input
7
 PORTB |=  LCD_RW; // switch display to read-mode
8
  e8:  c2 9a         sbi  0x18, 2  ; 24
9
 PORTB &= ~LCD_RS; // clear RS, this is always a command
10
  ea:  c3 98         cbi  0x18, 3  ; 24
11
 PORTB |=  LCD_E;  // set E
12
  ec:  c1 9a         sbi  0x18, 1  ; 24
13
 x = PINA & 0x08;  // we only need the busy-flag
14
  ee:  89 b3         in  r24, 0x19  ; 25
15
 PORTB &= ~LCD_E;  // clear E
16
  f0:  c1 98         cbi  0x18, 1  ; 24
17
 // read second nibble to complete access
18
 PORTB |=  LCD_E;  // set E
19
  f2:  c1 9a         sbi  0x18, 1  ; 24
20
 PORTB &= ~LCD_E;  // clear E
21
  f4:  c1 98         cbi  0x18, 1  ; 24
22
  f6:  88 70         andi  r24, 0x08  ; 8
23
 return(x);
24
}
25
  f8:  99 27         eor  r25, r25
26
  fa:  08 95         ret

Nicht nur, dass dabei völlig anderer Code rauskommt bei dem ich mich 
frage, wieso nicht auch bei -O0 zumindest sbi und cbi auftauchen.
Warum wird denn bitteschön das "DDRA = 0x00;" rausgeworfen?

Netter Fehler ist auch, dass die Bit-Maskierung mit 0x08 wegoptimiert 
wird, ein Test "while(display_busy());" macht dann auch nicht mehr, was 
es soll.

Und was macht man dagegen?
Einfach nicht optimieren lassen?
Der Code der dann rauskommt ist ja selbst funktionsfähig nicht witzig.

von Rudolph R. (rudolph)


Lesenswert?

Sorry, am Anfang überlegt, am Ende nicht dran gedacht...

Das ist mit WinAVR und einem ATMega16 als Target im AVR-Studio.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rudolph R. wrote:

> Nicht nur, dass dabei völlig anderer Code rauskommt bei dem ich mich
> frage, wieso nicht auch bei -O0 zumindest sbi und cbi auftauchen.

Kurze Antwort: Weil das halt eine Optimierung ist, und die hast du mit
-O0 ausdrücklich verboten.

Lange Antwort: Die IO-Ports der AVRs kann man auf zwei verschiedenen
Wegen ansprechen.  Einerseits bilden sie einen IO-Adressraum für
spezielle Befehle (IN/OUT/SBI/CBI/SBIS/CBIS).  Dieser IO-Adressraum
ist limitiert auf 64 Adressen, für einen Teil der Befehle sogar auf
nur 32 Adressen.  Andererseits werden alle IO-Ports (auch die
jenseits der IO-Adresse 63) im SRAM-Adressraum abgebildet, dummerweise
bei den derzeitigen AVRs mit einem Offset von 0x20 zu den IO-Adressen.
Daher bleibt dem Compiler für IO-Zugriffe erst einmal nichts anderes
übrig als die worst-case-Annahme, dass man sie über den SRAM-Bereich
zugreifen muss, denn nur diese Methode funktioniert immer.  Der
Zugriff über die speziellen IO-Befehle ist dann anschließend eine
Optimierung, die nur dann eingebaut werden kann, wenn das für den
konkreten Fall an Hand der gegebenen IO-Adresse auch tatsächlich
möglich ist.

> Warum wird denn bitteschön das "DDRA = 0x00;" rausgeworfen?

Was macht dich glauben, dass es das würde?  Ich sehe es ganz deutlich
auf Adresse 0xe6.

> Netter Fehler ist auch, dass die Bit-Maskierung mit 0x08
> wegoptimiert wird, ...

Ach?  Und was steht auf Adresse 0xf6?

> Und was macht man dagegen?

Dein Timing an die Anforderungen des LC-Displays anpassen, denn da
liegt dein Hund im Pfeffer oder der Hase begraben oder sowas: solange
du die Optimierung ausschaltest, ist der generierte Code langsam genug
für das Display.  Mit Optimierung hälst du einfach mal das
vorgeschriebene Timing nicht mehr ein.

Was glaubst du, warum schon zig Programmierer vor dir ihre
LCD-Bibliotheken nach all dem Haarerausraufen am Ende lieber
veröffentlicht haben?  Damit man das Rad nicht nochmal erfinden muss.
Nichts dagegen, dass du es trotzdem machst, aber dann such die Fehler
bitte als erstes zwischen Tastatur und Stuhl und nutze das als
Möglichkeit, mit solchen Dingen umgehen zu lernen.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Der Code macht bei -O0 und -O1 das gleiche, ABER bei -O1 wird es viel 
schneller gemacht. Bang Bang kommen die Signale auf den einzelnen 
Leitungen direkt hintereinander.

Ich kann mur gut vorstellen, dass dadurch das im 
LCD-Controller-Datenblatt angegebene Timing NICHT eingehalten wird. Bei 
-O0 sind gut 4-6 Instruktionen mehr zwischen dem Umschalten der drei 
Steuerleitungen und dem Umschalten und dem Einlesen.

Probier doch mal im -O1 Fall zwischen die Statements eine Handvoll asm 
volatile ("nop"); reinzusetzen

EDIT1: Jörg war schneller.

EDIT2: Die nops nur als Q&D um die Ursache aufzuklären. Um stabilen, 
CPU-frequenzunabhängigen Code zu bekommen, ist das natürlich Mumpitz

von Rudolph R. (rudolph)


Lesenswert?

Jörg Wunsch wrote:
> Lange Antwort: Die IO-Ports der AVRs kann man auf zwei verschiedenen
> Wegen ansprechen.
[...]

Durchaus interessant, dazu fällt mir dann aber letztlich nur ein, dass 
ich in C Programmiere, um davon nicht viel wissen zu müssen. ;-)

>> Warum wird denn bitteschön das "DDRA = 0x00;" rausgeworfen?
>
> Was macht dich glauben, dass es das würde?  Ich sehe es ganz deutlich
> auf Adresse 0xe6.

Okay, offenbar habe ich das mit Assembler nicht im Griff.
Was mich aber wundert ist, wie die 0x00 dann in R1 kommt.

>> Netter Fehler ist auch, dass die Bit-Maskierung mit 0x08
>> wegoptimiert wird, ...
>
> Ach?  Und was steht auf Adresse 0xf6?

Ein Beleg für meine Blindheit?

>> Und was macht man dagegen?
>
> Dein Timing an die Anforderungen des LC-Displays anpassen, denn da
> liegt dein Hund im Pfeffer oder der Hase begraben oder sowas: solange
> du die Optimierung ausschaltest, ist der generierte Code langsam genug
> für das Display.  Mit Optimierung hälst du einfach mal das
> vorgeschriebene Timing nicht mehr ein.

Aua, ich muss dringend mal wieder einen Blick in das Datenblatt der 
Displays werfen.
Aufgebaut habe ich das ja schon ein paar Mal aber offenbar habe ich 
bisher immer "-O0" verwendet.

Da werde ich dann wohl ein paar mehr NOP's brauchen, mit 1 MHz statt mit 
8 MHz läuft der Code nämlich auch nicht.

> Was glaubst du, warum schon zig Programmierer vor dir ihre
> LCD-Bibliotheken nach all dem Haarerausraufen am Ende lieber
> veröffentlicht haben?  Damit man das Rad nicht nochmal erfinden muss.

Ist aber langweilig, immer nur Bibliotheken zu benutzen.
Wobei ich den Code auch zum fünften oder sechsten Mal benutze...

Als nächstes dann TWI...

> Nichts dagegen, dass du es trotzdem machst, aber dann such die Fehler
> bitte als erstes zwischen Tastatur und Stuhl und nutze das als
> Möglichkeit, mit solchen Dingen umgehen zu lernen.

Daher ja die Frage im Thema "- was mache ich falsch?" :-)

Vielen Dank!

von Rudolph R. (rudolph)


Lesenswert?

Hmm, nach ein paar NOPs und einem scharfen Blick ins Datenblatt läuft es 
jetzt durch einen einzigen zusätzlichen NOP auch mit 8 MHz.
1
uint8_t display_busy(void)
2
{
3
 uint8_t x;
4
5
 DDRA = 0x00;      // set all Pins from PORTA to input
6
 PORTB |=  LCD_RW; // switch display to read-mode
7
 PORTB &= ~LCD_RS; // clear RS, this is always a command
8
 PORTB |=  LCD_E;  // set E
9
   asm volatile ("nop"); // give display some time to react
10
 x = PINA & 0x08;  // we only need the busy-flag
11
 PORTB &= ~LCD_E;  // clear E
12
 // read second nibble to complete access
13
 PORTB |=  LCD_E;  // set E
14
 PORTB &= ~LCD_E;  // clear E
15
 return(x);
16
}

Um das sauber zu bekommen muss ich aber wohl noch tiefer graben.
In dem Datenblatt von Hitachi für den HD44780 steht eine maximale 
Ausführungszeit für das Lesen des Busy-Flags von "0 µs".

Offensichtlich muss aber doch Zeit vergehen vom Setzen von E bis die 
Daten da sind, nicht wirklich viel wenn ein NOP reicht, aber genug, um 
nicht ohne auszukommen.

Edit: okay, tER sind 20 ns und tDDR sind 160 ns.
Also muss man schon 180 ns warten nachdem E auf High gegangen ist.
-> mit 4 NOPs läuft das auch bei 20 MHz. ]:-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rudolph R. wrote:

>> Lange Antwort: Die IO-Ports der AVRs kann man auf zwei verschiedenen
>> Wegen ansprechen.
> [...]

> Durchaus interessant, dazu fällt mir dann aber letztlich nur ein, dass
> ich in C Programmiere, um davon nicht viel wissen zu müssen. ;-)

Musst du ja auch nicht, denn dafür erledigt das der Optimierer für dich.

> Was mich aber wundert ist, wie die 0x00 dann in R1 kommt.

Da die Konstante 0 sehr häufig benötigt wird, belegt der Compiler
per Konvention (seines ABIs) ein Register permanent mit dieser
Konstante.  Innerhalb des Compilers besitzt r1 den Aliasnamen
_zero_reg_ dafür.  Diese Vorgehensweise ist bei RISC-Architekturen
nicht unüblich, manche RISC-CPUs besitzen zuweilen dafür sogar ein
hart auf 0 verdrahtetes Register.

(Die Wahl von r1 hat sich im Nachhinein mit der Einführung der
MUL-Befehle als nicht sehr glücklich erwiesen, aber eine Umstellung
des ABI ist ein ziemlicher Klimmzug, da er mit vielem existierenden
Code bricht.)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rudolph R. wrote:

> Offensichtlich muss aber doch Zeit vergehen vom Setzen von E bis die
> Daten da sind, nicht wirklich viel wenn ein NOP reicht, aber genug, um
> nicht ohne auszukommen.

Du stolperst über ein weiteres AVR-Detail hier (bin ich bei meiner
LCD-Bibliothek damals auch, obwohl ich es eigentlich bereits
wusste): das Samplen der Daten, wenn der nächste Befehl ein port
input ist, erfolgt bereits bevor der port output des aktuellen
Befehls am Ausgang anliegt (und zwar einen halben CPU-Takt davor).
Daher muss man, wenn der Wert einer Port-Eingabe von einer
unmittelbar vorangehenden Port-Ausgabe abhängt, wenigstens einen
weiteren Befehl zwischen beiden einfügen.

von Falk B. (falk)


Lesenswert?

@ Rudolph R. (rudolph)

>Hmm, nach ein paar NOPs und einem scharfen Blick ins Datenblatt läuft es
>jetzt durch einen einzigen zusätzlichen NOP auch mit 8 MHz.

Oder einfach solide, wie im Tutorial.

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmierung

Wobei das BUSY Flag so ziemlich kein Mensch braucht.

MFG
Falk

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Falk Brunner wrote:

> Wobei das BUSY Flag so ziemlich kein Mensch braucht.

Begründung?

Die worst-case-Wartezeiten sind laut Datenblatt um einiges
pessimistischer angesetzt, als die tatsächliche Abarbeitung oft
dauert.

von Andreas K. (a-k)


Lesenswert?

Jörg Wunsch wrote:

> Begründung?

Was für Wettrennen veranstaltest du auf einem Text-LCD denn so? Ein 4x16 
Display kannst du auch ohne Ready 300mal pro Sekunde vollschreiben.

von Rudolph R. (rudolph)


Lesenswert?

Falk Brunner wrote:
>>Hmm, nach ein paar NOPs und einem scharfen Blick ins Datenblatt läuft es
>>jetzt durch einen einzigen zusätzlichen NOP auch mit 8 MHz.
>
> Oder einfach solide, wie im Tutorial.
>
> http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmierung

Daran ist doch garnichts besonders solide.
1
 LCD_PORT |= (1<<LCD_EN);
2
 _delay_us(1);                   // kurze Pause
3
 // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern

Da kann ich auch genausogut nach Datenblatt und Takt NOP's reinhängen.

> Wobei das BUSY Flag so ziemlich kein Mensch braucht.

Kommt darauf an.
Ich kann das "_delay_us(42);" aus dem Tutorial nicht gebrauchen.
Das ist dann auch nichts weiter als geraten.
Und am Ende des Display-Codes besonders bitter, finde ich.

Mein Code läuft weitgehend unabhängig vom Display.
Ich benutze ein Array als "Bildschirm-Puffer" in das ich jederzeit von 
beliebigen Programm-Teilen aus schreiben kann.
Eine Zeitscheibe in meinem "Scheduler" fragt das Busy-Flag ab.
Ist das Display bereit, wird das nächste Zeichen des Puffers gesendet.
Ist das Display nicht bereit, wird die Zeitscheibe beendet.

Die folgenden Zeitscheiben können ungebremst und beliebig oft in das 
Array schreiben und es ist völlig egal, was das Display gerade so 
treibt.


@Jörg Wunsch

Vielen Dank für die Informationen und die Geduld!
Und vor allem auch vielen Dank für WinAVR!

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rudolph R. wrote:

> Und vor allem auch vielen Dank für WinAVR!

Da kann ich leider nichts dafür, ich benutze kein Windows. ;-)  Das geht
ganz allein auf Eric Weddingtons Kappe...  OK, aber ich bin bei einigen
der Dinge beteiligt, die Eric da zusammenpackt.

von Falk B. (falk)


Lesenswert?

@ Rudolph R. (rudolph)

>Da kann ich auch genausogut nach Datenblatt und Takt NOP's reinhängen.

Klar, weil man das dann a) per Hand berechen darf wieviel Mikrosekunden 
das sind und b) das dann immer wieder neu machen darf, wenn man eine 
andere Taktfreqeunz verwendet.

>Ich kann das "_delay_us(42);" aus dem Tutorial nicht gebrauchen.
>Das ist dann auch nichts weiter als geraten.

Nöö, das sind garantierte Maximalzeiten aud dem Datenblatt.

>Und am Ende des Display-Codes besonders bitter, finde ich.

???

>Mein Code läuft weitgehend unabhängig vom Display.

Na dann lass es doch einfach weg ;-)

>Ich benutze ein Array als "Bildschirm-Puffer" in das ich jederzeit von
>beliebigen Programm-Teilen aus schreiben kann.
>Eine Zeitscheibe in meinem "Scheduler" fragt das Busy-Flag ab.
>Ist das Display bereit, wird das nächste Zeichen des Puffers gesendet.
>Ist das Display nicht bereit, wird die Zeitscheibe beendet.

Und? Applause gefällig? Nur mal zur Information. Das ist ein TUTORIAL, 
das die prinzipielle Funktion einem Anfänger näher bringen soll. Keine 
HighTec Lösung.

MFG
Falk

von Rudolph R. (rudolph)


Lesenswert?

Falk Brunner wrote:
>>Da kann ich auch genausogut nach Datenblatt und Takt NOP's reinhängen.
>
> Klar, weil man das dann a) per Hand berechen darf wieviel Mikrosekunden
> das sind und b) das dann immer wieder neu machen darf, wenn man eine
> andere Taktfreqeunz verwendet.

Du hast ja Recht - aber inwieweit unterscheidet sich das jetzt zu der 
Lösung im Tutorial?
Wie ich schon schrieb, ich kann auch jetzt 4 NOP's einbauen und es 
dürfte bis 20 MHz laufen.

>>Ich kann das "_delay_us(42);" aus dem Tutorial nicht gebrauchen.
>>Das ist dann auch nichts weiter als geraten.
>
> Nöö, das sind garantierte Maximalzeiten aud dem Datenblatt.

Aus einem Datenblatt doch wohl eher und ja, es ist ein Tutorial.

>>Und am Ende des Display-Codes besonders bitter, finde ich.
>
> ???

So, fertig, jetzt noch die restlichen 95 Prozent der Ausführungszeit 
abbummeln.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Falk Brunner wrote:

> Und? Applause gefällig? Nur mal zur Information. Das ist ein TUTORIAL,
> das die prinzipielle Funktion einem Anfänger näher bringen soll. Keine
> HighTec Lösung.

Dann brauchst du aber auch nicht behaupten, das ready-Bit wäre
überflüssig, insbesondere wenn es jemand tatsächlich sinnvoll
benutzt (weil er den Rest der Zeit die CPU was besseres tun lassen
kann).

von Falk B. (falk)


Lesenswert?

@ Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite

>überflüssig, insbesondere wenn es jemand tatsächlich sinnvoll
>benutzt (weil er den Rest der Zeit die CPU was besseres tun lassen
>kann).

Und wieviel schneller ist das dann real?

Mfg
Falk

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Falk Brunner wrote:

> Und wieviel schneller ist das dann real?

Das sollte Rudolph sagen können, hängt wohl einfach davon ab, was man
sonst noch zu tun hat.  Zumindest kann man im Zweifelsfall die CPU
schlafen legen und den Strom sparen, wenn man nix mehr zu tun hat.

von Andreas K. (a-k)


Lesenswert?

Jörg Wunsch wrote:

> (weil er den Rest der Zeit die CPU was besseres tun lassen kann).

Dann wiederum ist es sinnvoller, die LCD-Operationen per Timer-Interrupt 
(Scheduler) zu implementieren, also beispielsweise alle 2ms eine 
LCD-Operation. Das ist immer noch schnell genug und die Laufzeit einer 
LCD-Operation reduziert sich auf ~2µs.

von Rudolph R. (rudolph)


Lesenswert?

Andreas Kaiser wrote:
> Dann wiederum ist es sinnvoller, die LCD-Operationen per Timer-Interrupt
> (Scheduler) zu implementieren, also beispielsweise alle 2ms eine
> LCD-Operation. Das ist immer noch schnell genug und die Laufzeit einer
> LCD-Operation reduziert sich auf ~2µs.

Genau das habe ich doch gemacht - wie weiter oben beschrieben.
Nur fragt mein Code vorher beim Display nach, ob es bereit ist, Daten 
anzunehmen, statt sich drauf zu verlassen, dass das schon irgendwie 
passt.

Mein Hauptproblem war, dass ich von verschiedenen Zeitscheiben aus auf 
verschiedene Teile des Displays zugreifen wollte, ohne das die sich 
gegenseitig blockieren.

Wie schnell das Display mit Daten versorgt wird ist mir ziemlich egal - 
so lange es halt nicht 1 "Frame" in 2 Sekunden wird.

Wie schnell die restlichen Teile des Programms reagieren können, war mir 
allerdings nicht egal.

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.