Forum: Mikrocontroller und Digitale Elektronik LED-CUBE von leyanda mit Atmega644P


von Sven (Gast)


Lesenswert?

Hallo,

ich habe den Cube von Leyanda nachgebaut. Allerdings habe ich einen 
Atmega644P statt einem Atmega32.
In der main.c habe ich
1
#define F_CPU 16000000UL
hinzugefügt. Jetzt muss ich noch die Timer in ledcube.c anpassen. Damit 
sollte es das dann ja gewesen sein oder?
aus
1
void cube_show_init( void )
2
{
3
  TCCR0 |= (1 << CS02) | (0 << CS00); 
4
  TIMSK |= (1 << TOIE0);
5
}
6
7
8
ISR( TIMER0_OVF_vect )
9
{
10
  TCNT0 = 0xB0; /*at least 0xA0 */
11
  /* cli() not neccessary atm */
12
  PORTC = 0x0;
13
  static uint8_t cube_show_layer = 0;
14
....
habe ich in meinem Unwissen
1
void cube_show_init( void )
2
{
3
  TCCR0A |= (1<<CS02)|(0<<CS00);   
4
  TCCR0B |= (1<<CS02)|(0<<CS00);
5
  TIMSK0 |= (1 << TOIE0);      
6
}
7
8
9
ISR( TIMER0_OVF_vect )
10
{
11
  TCNT0 = 0xB0; /*at least 0xA0 */
12
  /* cli() not neccessary atm */
13
  PORTC = 0x0;
14
  static uint8_t cube_show_layer = 0;
gemacht. Allerdings läuft irgendwie alles viel viel zu schnell ab. Was 
muss ich noch ändern? Oder kann das noch etwas mit den Fuses zu tun 
haben?

Danke für eure Hilfe.

von Sven (Gast)


Lesenswert?

hier noch der Link zum Projekt von mikrocontroller.net:
Titelhttp://www.mikrocontroller.net/articles/LED_cube

von Karl H. (kbuchegg)


Lesenswert?

Sven schrieb:

> habe ich in meinem Unwissen
>
> void cube_show_init( void )
> {
>   TCCR0A |= (1<<CS02)|(0<<CS00);
>   TCCR0B |= (1<<CS02)|(0<<CS00);
>   TIMSK0 |= (1 << TOIE0);
> }

Und was soll das sein?
Die CS02, CS00 Bits werden ja wohl nicht in beiden Registern vorliegen. 
Das wäre ein bischen dämlich von Atmel. Denn: welches Register gilt denn 
jetzt? Welcher Satz von Bits steuert denn den Vorteiler, wenn die sowohl 
in TCCR0A als auch in TCCR0B vorliegen?

Du musst schon auch in die Datenblätter schauen und nicht einfach aufs 
Geratewohl die Dinge so schreiben wie du dir denkst dass es sein könnte.

Du musst begreifen, dass es da keinen Schutz gibt. Letzten Endes sind 
diese Bitnamen einfach nur andere Namen für Bitmuster. Der Compiler kann 
nicht überprüfen ob es in diesem Register ein Bit mit diesem Namen gibt. 
Der schreibt einfach nur das sich durch die Konstanten ergebenden 
Bitmuster ins Register. Was immer das dann auch bewirkt!
Es ist dein Bier, dass die Kombination aus Registernamen und Bitname 
'gültig' ist und das gewünschte bewirkt. Und wenn du dich da vertust, 
dann setzt du eben irgendein anderes Bit, welches in diesem Register 
'zufällig' an genau dieser Bitposition ist. Und das schaltet dann eben 
irgendwas ein oder aus. Was genau ... das steht im Datenblatt bei der 
Beschreibung des Register. Dort sieht man, was ein gesetztes 0-tes Bit 
wirklich macht.

von Karol B. (johnpatcher)


Lesenswert?

Um Karl Heinz Bucheggers Ausführungen konkreter zu machen: Die 
Prescalereinstellungen beim ATmega644 verstecken sich laut Datenblatt im 
Register TCCR0B. Das eigentliche Bitmuster "(1<<CS02)|(0<<CS00)" kannst 
du aber übernehmen, um den gleichen Prescaler (= 1024) zu erhalten.

Du schreibst dieses Bitmuster aber auch in TCCR0A, wodurch du den Modus 
des Timer/Counters veränderst.

Sven schrieb:
> Jetzt muss ich noch die Timer in ledcube.c anpassen. Damit
> sollte es das dann ja gewesen sein oder?

Das wird dir kaum jemand beantworten können, da wohl niemand den 
Quellcode (und die Unterschiede) zwischen den beiden Controllern 
"auswendig" kennt. Application Note AVR505 
(http://www.atmel.com/Images/doc8001.pdf) könnte hier hilfreich sein und 
listet die Unterschiede zwischen den beiden Controllertypen auf.

Ich weiß natürlich nicht was für Programmier-Kenntnisse du so mitbringst 
und inwiefern du Lust zum Basteln hast, aber ich persönlich finde das 
Software-Konzept von o.g. Projekt nicht besonders schön. Bzw. um es 
anders zu sagen: Es gibt bessere Konzepte. So z.B. dieses 
(Beitrag "LED cube 8x8x8"). Hier wird mit einem 
Doppelpufferung gearbeitet, wodurch die Bildfrequenz stabil ist und es 
zu keinerlei Flackereffekten kommt, welche der Autor von o.g. Projekt ja 
im Quellcode selbst bemängelt. Überhaupt kommt mir persönlich der 
Quellcode sauberer und überschaubarer vor.

Allerdings unterscheidet sich der Hardwareaufbau und insofern gäbe es da 
schon einiges anzupassen. Das Konzept allerdings (sowie die Animationen) 
würden sich wunderbar übernehmen lassen.

Sven schrieb:
> Oder kann das noch etwas mit den Fuses zu tun
> haben?

Es schadet sicherlich nicht sich die Fuses mal anzusehen und eventuell 
auftretende Unterschiede zu untersuchen. O.g. Application Note 
beinhaltet auch ein Kapitel bzgl. der Fuses.

von Sven (Gast)


Lesenswert?

vielen Dank für die Hinweise. Das mit den mangelhaften 
Programmierkenntnissen ist wohl das größte Problem :-D Deshalb ist mir 
auch nicht so ganz klar was das Datenblatt mir vermitteln möchte.

Das Thema mit den Timern kommentieren wir einmal kurz aus. Ich habe 
momentan eher den Eindruck, dass die delays zu schnell laufen, bzw von 
einer falschen Frequenz ausgegangen wird. Allerdings verstehe ich nicht 
warum. Anschließendd widme ich mich gerne nochmal dem Timerproblem und 
damit der Interruptroutine.

das #define F_CPU habe ich wieder gelöscht. Hat keine Auswirkungen. Aber 
wie stelle ich dann die Freuenz ein? Sonst kann das mit den delays ja 
nie was werden wenn ich das richtig verstanden habe?

von Paul Baumann (Gast)


Lesenswert?

Merkt das der C-Kompiler nicht, wenn die Bitnamen in diesem Register
nicht existieren?

MfG Paul

von Sven (Gast)


Lesenswert?

Beim ersten mal hat er sich beschwert. Deshalb habe ich die Zeile 
hinzugefügt. Wenn ich sie jetzt lösche beschwert er sich nicht mehr...

von Karol B. (johnpatcher)


Lesenswert?

Paul Baumann schrieb:
> Merkt das der C-Kompiler nicht, wenn die Bitnamen in diesem Register
> nicht existieren?

Und welcher "C-Kompiler" kann das? Siehe oben gemachte Ausführungen von 
"Karl Heinz Buchegger".

von Sven (Gast)


Lesenswert?

Ich hätte mir wohl einmal diese Output-meldungen vom Compiler genauer 
ansehen sollen. Ich bin bisher immer davon ausgegangen, dass bei "1 
succeeded, 0 failed, 0 skipped" schon alles in Ordnung wäre. Von den 
ganzen Meldungen über den Compilerverlauf verstehe ich im Normalfall 
keine, die gemacht werden. Deshalb achte ich immer nur auf die 
Fehlermeldungen wie "; expected" etc..
Allerdings war diesmal im Text die Warnung versteckt, dass F_CPU nicht 
definiert ist. Diese Warnungen bekomme ich nur weg, wenn ich in alle 
Dateien "#define F_CPU" hinzufüge. Sollte das in der main.c nicht 
übergeordnet gehen? Oder erstellt man hierfür noch eine .h die überall 
includiert wird?

Die Compiler Optimization habe ich auch noch von -O1 auf -Os gestellt. 
Leider kann ich erst wieder am Montag testen.

Ich hoffe damit habe ich die meisten Anfängerfehler erschlagen.

Die Timereinstellungen für die Interruptroutine habe ich jetzt einfach 
mal aus dem AVR-GCC Tutorial: Timer/Zähler kopiert.
1
TCCR0A = (1<<WGM01); // CTC Modus
2
TCCR0B |= (1<<CS02)|(1<<CS00); // Prescaler 1024
3
OCR0A = 125-1;       // Vergleichswert (noch anpassen)
4
// Compare Interrupt erlauben
5
TIMSK |= (1<<OCIE0A);
6
7
ISR (TIMER0_COMPA_vect)
8
{
9
...

von Paul Baumann (Gast)


Lesenswert?

Karol Babioch schrob:
>Und welcher "C-Kompiler" kann das?

Das weiß ich nicht, deshalb frage ich ja. Ich kenne "C" nicht und unter
solchen Umständen möchte ich es dann auch gar nicht kennen lernen.

>Siehe oben gemachte Ausführungen von "Karl Heinz Buchegger".

Wenn das daraus hervorgegangen wäre, hätte ich die Frage nicht 
gestellt...

MfG Paul

von Karl H. (kbuchegg)


Lesenswert?

Sven schrieb:

> Allerdings war diesmal im Text die Warnung versteckt, dass F_CPU nicht
> definiert ist. Diese Warnungen bekomme ich nur weg, wenn ich in alle
> Dateien "#define F_CPU" hinzufüge. Sollte das in der main.c nicht
> übergeordnet gehen? Oder erstellt man hierfür noch eine .h die überall
> includiert wird?

Solche #define, die quer über das ganze Projekt gelten sollen, macht man 
am besten in den Projekteinstellungen.
Denn auch dafür hat man den Überbau eines Projektes, auch wenn es so 
etwas in C in der Form nicht gibt.
Praktisch jeder Compiler hat eine Möglichkeit, wie man ihm beim Aufruf 
einige #define mitgeben kann. Hat man also in seinen 
Projekteinstellungen eingetragen, dass dem Compiler beim starten des 
Compiliervorganges mitgegeben werden soll, dass er das Makro-Symbol 
F_CPU mit dem Text "8000000" vorbelegen soll, dann ist dadurch 
garantiert, dass alle C-Files mit genau diesem #define compiliert 
werden.

Im AVR-Studio-4 gab es dazu in den Projektoptionen sogar ein eigenes 
Feld, wo man für F_CPU den Wert eintragen kann. Im 6-er Studio ist 
dieses spezielle Feld aus mir unverständlichen Gründen rausgeflogen 
(meiner Meinung nach ein Schritt in die falsche Richtung), aber die 
Möglichkeit gibt es natürlich immer noch, weil man den Eintrag natürlich 
bei den Symbolen machen kann, die dem Compiler mitgegeben werden.


> Die Timereinstellungen für die Interruptroutine habe ich jetzt einfach
> mal aus dem AVR-GCC Tutorial: Timer/Zähler kopiert.

Das ist KEINE gute Idee.
Also, das "Ich kopiere das einfach"

Gewöhne dir an, dass das Datenblatt deines µC deine Bibel ist. Das ist 
die einzige Autorität, die du akzeptierst! Es ist wirklich nicht schwer, 
sich im jeweiligen Timer-Kapitel im Abschnitt "Register Summary" die 
Dinge zusqammenzuholen, die man beabsichtigt. In der FAQ
FAQ: Timer
hab ich in diesem Abschnitt versucht, den Prozess des "wie gehe ich vor" 
zu skizzieren.
Aber mit dem "Ich übernehm das eigentlich und kontrolliere nichts mit 
dem Datenblatt" wirst du auf die Schnauze fallen! Und zwar immer und 
immer wieder! Und dann suchst du dir einen Wolf. Die Einstellungen zu 
kontrollieren bzw. sich aus dem Datenblatt rauszuholen, dauert (wenn man 
erst mal weiß was man eigentlich einstellen will) keine 2 Minuten! Diese 
2 Minuten können dir aber Stunden der Fehlersuche ersparen.

von Karl H. (kbuchegg)


Lesenswert?

Paul Baumann schrieb:
> Karol Babioch schrob:
>>Und welcher "C-Kompiler" kann das?
>
> Das weiß ich nicht, deshalb frage ich ja. Ich kenne "C" nicht und unter
> solchen Umständen möchte ich es dann auch gar nicht kennen lernen.
>
>>Siehe oben gemachte Ausführungen von "Karl Heinz Buchegger".
>
> Wenn das daraus hervorgegangen wäre, hätte ich die Frage nicht
> gestellt...


Um das weiter auszuführen:
Nein, das kann er so erst mal nicht.

Denn

   TCCR0B |= (1<<CS02)|(1<<CS00); // Prescaler 1024

ist für den Compiler erst mal nur eine Zuweisung eines Zahlenwertes an 
eine Variable.

   TCCR0B |= 0b00000101;

ist genau dasselbe: Ein Zahlenwert, diesmal ausgedrückt in Form von 
Bits, wird an das Register zugewiesen

   TCCR0B |= 0x05;

ist genau das gleiche, nur das der Zahlenwert in einer anderen Form 
(diesmal als Hex-Schreibweise) angegeben ist.

Und genauso ist in

   TCCR0B |= (1<<CS02)|(1<<CS00); // Prescaler 1024

der Teil rechts vom |= einfach nur eine andere Schreibweise für diesen 
Zahlenwert. Man könnte auch schreiben

   TCCR0B |= 4 + 1;
oder
   TCCR0B |= 2*2 + 1;

denn letzten Endes ist es ja wurscht was auf der rechten Seite steht, 
solange nur aus diesem Ausdruck sich das Bitmuster ergibt, welches man 
in das Register verodern will. Kurz und gut: die rechte Seite der 
Zuweisung hängt so erst mal nicht mit der linken Seite zusammen. Die 
rechte Seite der Zuweisung kommt mit einem Bitmuster hoch, die linke 
Seite bestimmt was damit geschehen soll. Zwischen den beiden gibt es 
erst mal keinen weiteren logischen Zusammenhang.


Aber:
* das ganze klingt jetzt schlimmer als es ist. Mit ein wenig Sorgfalt 
ist das in der Praxis kein Problem. Man muss einfach nur die Attitüde: 
"Mir wurscht was im Datenblatt steht, ich übernehm den Code einfach so 
wie ich ihn sehe" aufgeben
* man kann sich natürlich das ganze auch so zurecht legen, dass der 
Compiler die Überprüfung machen kann. Hey - wir reden hier von C! Wenn 
man seine Sprache kennt, kann man vieles erreichen, was einem 
Nicht-Kenner erst mal graue Haare bereitet. C bringt die Mittel und die 
Möglichkeiten mit. Man muss sie nur benutzen. Es hat schon seinen Grund 
warum es C-Compiler vom kleinsten 8-Bit µC bis hinauf zum stärksten 
128-Bit Supercomputer gibt! Und die Sprache C ist in dieser ganzen 
Bandbreite von Rechnern immer die gleiche. Ab und zu angereichert mit 
Spezialitäten, die nur für eine bestimmte Plattform relevant sind, aber 
wer einmal C von der Pieke auf gelernt hat (nicht so wie hier im Forum, 
wo immer wieder Leute aufschlagen, die gerade mal 15% des Sprachumfangs 
beherrschen), der kommt in kürzester Zeit auf all diesen Plattformen 
zurecht.

von Paul Baumann (Gast)


Lesenswert?

@Karl-Heinz

Danke für diese ausführliche Erklärung. Ich dachte zunächst, daß man
wie z.B. in Assembler oder auch in Bascom auch in C eine Datei in
das Programm einbinden muß, die die Registerdefinitionen beinhaltet.

So wie das hier:
$regfile = "m32def.dat"
oder in Assembler:
.include "4433def.inc"

Wenn ich dann dort aus Versehen ein Bit in einem falschen Register 
"vermute", dann gibt es eben eine Fehlermeldung in der Art:
"Dieses Bit existiert im Register "Trallalla" nicht.

Das finde ich besser, als in "C" wo offenbar dann kein Fehler erkannt
wird und das Programm sich trotzdem kompilieren läßt, aber dadurch
natürlich die "lustigsten" Sachen macht...

MfG Paul

von spess53 (Gast)


Lesenswert?

Hi

>Wenn ich dann dort aus Versehen ein Bit in einem falschen Register
>"vermute", dann gibt es eben eine Fehlermeldung in der Art:
>"Dieses Bit existiert im Register "Trallalla" nicht.

Von Assembler kenne ich das nicht so. Da wird nur gemeckert wenn es 
diesen Bitbezeichner überhaupt nicht gibt.

MfG Spess

von Sven (Gast)


Lesenswert?

Im Moment bin ich mit der "Bibel" noch etwas überfordert. Dafür habe ich 
einfach zu wenig Ahnung von der ganzen Materie. Ich verfolge momentan 
das Motto: learning by doing. Vieles klappt dann aber doch ohne, dass 
ich dabei etwas gelernt oder verstanden habe...

Im Nachhinein hätte ich mir statt Maschinenbau ein Studium mit deutlich 
stärkerem Aspekt auf der Elektrotechnik suchen sollen. Wenn man sich ein 
bisschen damit beschäftigt kann das ja fast schon Spaß machen. :-D

Ich habe viele Artikel von dieser Seite schon mehrfach gelesen und finde 
das meiste viel verständlicher als diverse Büchern, die ich ausgeliehen 
habe. Über die FAQ bin ich aber irgendwie noch nie gestolpert. Hilft auf 
jeden Fall für das Gesamtverständnis.

von Karl H. (kbuchegg)


Lesenswert?

Paul Baumann schrieb:


> So wie das hier:
> $regfile = "m32def.dat"

Bascom

> oder in Assembler:
> .include "4433def.inc"

Assembler

>
> Wenn ich dann dort aus Versehen ein Bit in einem falschen Register
> "vermute", dann gibt es eben eine Fehlermeldung in der Art:
> "Dieses Bit existiert im Register "Trallalla" nicht.

In Bascom.
Weil Bascom speziell an die AVR angepasst wurde.

Aber C ist ja eine Universalsprache. Und Atmel hat sich die Dinge da ein 
wenig einfach gemacht und die simpelst mögliche Alternative gewählt: Das 
Problem auf den Benutzer abwälzen. Es wär in C auch anders gegangen.

>
> Das finde ich besser, als in "C" wo offenbar dann kein Fehler erkannt
> wird und das Programm sich trotzdem kompilieren läßt, aber dadurch
> natürlich die "lustigsten" Sachen macht...


struct TCCR0_
{
  unsigned char CS00 : 1;
  unsigned char CS01 : 1;
  unsigned char CS02 : 1;
  unsigned char WGM01 : 1;
  unsigned char COM00 : 1;
  unsigned char COM01 : 1;
  unsigned char WGM00 : 1;
  unsigned char FOC0 : 1;
};

#define TCCR0 (*(volatile struct TCCR0_*)0x0053)
....


   TCCR0.CS00 = 1;    // Ist ok, CS00 gibt es im TCCR0

   TCCR0.WGM02 = 1;   // Möööp, WGM02 existiert nicht in TCCR0


und das ist jetzt nur EINE Möglichkeit. Man kann das Prinzip noch weiter 
treiben und landet im Endeffekt bei einem ähnlichen Komfort wie in 
BASCOM.

Alles nur eine Frage dessen, ob man als 'Hersteller' den Aufwand treiben 
will oder nicht. Das hat aber mit 'C' (als Sprache) an sich überhaupt 
nichts zu tun. C bietet alle Zutaten, die man braucht. C ist nur ein 
Werkzeug. Ob ein Benutzer dann dieses Werkzeug auch in allen seinen 
Fasetten einsetzen kann, darfst du nicht dem Werkzeug anlasten (und 
genau das tust du momentan. Du verwechselst die Fähigkeiten des 
Programmierers bzw. das was Atmel vorgefertigt hat, mit dem was sein 
Werkzeug eigentlich leisten könnte)

von Karl H. (kbuchegg)


Angehängte Dateien:

Lesenswert?

Sven schrieb:
> Im Moment bin ich mit der "Bibel" noch etwas überfordert.

Du bist damit überfordert, dir im Datenblatt die 5 Seiten rauszusuchen, 
auf denen die Register in all ihren Bits mit der genauen Beschreibung 
jedes einzelnen Bits erklärt werden? Bzw. mit zwischengestreuten 
Tabellen in denen die Bits zusammengefasst werden die Tabelle 
rauszusuchen, die für dich zutrifft?


Du bist damit überfordert, dir aus

(siehe Bild)

die für deinen gewünschten Modus notwendigen WGM Bits rauszulesen und 
dann bei 2 Registerbeschreibungen nachzusehen, in welchem Register 
welches Bit beheimatet ist?

(Selbiges für die Vorteiler-Einstellung)


Das ist jetzt aber nicht dein Ernst!
In deinem eigenen Interesse fasse ich das jetzt mal als billige Ausrede 
auf. Denn ansonsten müsste ich dir sagen: such dir ein anderes Hobby, 
wenn du damit überfordert bist, dir aus 2 Tabellen und 3 Absätzen die 
Information zusammenzusuchen.

von Paul Baumann (Gast)


Lesenswert?

@Karl-Heinz
Danke auch für den 2. Text von Dir. Dann weiß ich jetzt, daß man sich
zumindest als Ersteller des Programmes selbst eine Kontrollmöglichkeit
auf Fehler einbauen könnte, um sich nicht ungewollt zum Affen zu machen.

Natürlich guckt man erst mal im Datenblatt, was man überhaupt an
"Verhaltes-Steuer-Bits" braucht und wo die sich herumsielen, aber wie
schnell hat man mal einen falschen Buchstaben drin und schon ist der
Spaß vorbei, wenn das nicht abgefangen wird.


MfG Paul

von Sven (Gast)


Lesenswert?

ja ich bin überfordert, wäre aber erleichtert, wenn du mir sagst dass 
das in die richtige Richtung geht:
1
TCCR0A |= (0<<COM0A1)|(0<<COM0A0)|(1<<WGM01) //Normal port operation, OC0A disconnected, CTC-Modus
2
TCCR0B |= (1<<CS02)|(0<<CS00); //Prescaler 1024
3
OCR0A = 250;  // OCR0A wird mit TCNT0 verglichen
4
TIMSK0 |= (1<<OCIE0A); //Interrupt bei Match A (wenn OCF0A in TIFR0 gesetzt ist)
5
6
ISR( TIMER0_COMPA_vect)
7
{
8
...

irgendwie ist die Tabelle aus deinem Datenblatt übersichtlicher als die, 
die ich gerade verwende. Mit deiner Tabelle würde ich behaupten meine 
WGM sind falsch :-\
aber da seh ich gerade, dass das Bild zum Mega16 gehört. Liege ich dann 
vielleicht doch nicht so ganz daneben?

von K. B. (Gast)


Lesenswert?

Wieso versetzt du den Timer denn in den CTC Modus? In o.g. Projekt wird 
der Timer im ganz "normalen" Modus verwendet und von der Overflow ISR 
Gebrauch gemacht.

von Sven (Gast)


Lesenswert?

aber im Obigen Projekt wird der Timer auch manipuliert, um die 
Interruptintervalle anzupassen. Im Kommentar ist der alternative Weg CTC 
ja schon erwähnt. Da ich mich mit dem Thema jetzt schon auseinadersetze 
habe ich mich dann gleich dafür entschieden.
Gibt es Vor- und Nachteile der Varianten? Ich denke höchstens wenn ein 
Timer mehrere Funktionen erfüllen soll?

von Karol B. (johnpatcher)


Lesenswert?

Sven schrieb:
> Gibt es Vor- und Nachteile der Varianten? Ich denke höchstens wenn ein
> Timer mehrere Funktionen erfüllen soll?

In erster Linie sind die verschiedenen Modi halt für unterschiedliche 
Zwecke gedacht. In deinem Fall dürfte der CTC Modus schon mehr Sinn 
machen, um die Multiplexfrequenz besser bestimmen zu können. Allerdings 
musst du halt prüfen was in der "alten" Overflow-Routine so alles 
passiert. Es kann durchaus sein, dass du da mehr ändern musst, als den 
Namen des Interrupt Vektors.

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.