Nabend zusammen, werden im Tutorial absichtlich kleine Fehler eingebaut um den Anfängern das denken beizubringen? Konkret im Tutorial Uhr und CTC Modus. Im rcall lcd_number wird das Register temp1 nicht gesichert. In der Folge zeigt das LCD "Chinesische" Ziffern an. Original im Tutorial: lcd_number: push temp2 ldi temp2, '0' lcd_number_10: subi temp1, 10 brcs lcd_number_1 inc temp2 rjmp lcd_number_10 lcd_number_1: rcall lcd_data subi temp1, -10 ldi temp2, '0' add temp1, temp2 rcall lcd_data pop temp2 ret Bis man als Anfänger darauf kommt, dauert es etwas. Richtigerweise müsste dort stehen: lcd_number: push temp2 push temp1 mov temp2, temp1 ldi temp1, '0' lcd_number_10: subi temp2, 10 brcs lcd_number_1 inc temp1 rjmp lcd_number_10 lcd_number_1: rcall lcd_data subi temp2, -10 ldi temp1, '0' add temp1, temp2 rcall lcd_data pop temp1 pop temp2 ret Gerade Anfängern wird es hier schwer gemacht (fehlende Erfolge):-) Kann jemand den Code im Tutorial anpassen? Gruß Pebez
Ich kann ehrlich gesagt nicht wirklich nachvollziehen, was daran falsch sein soll. temp1 ist das Eingangsregister dieser Funktion. Der Wert in temp1 soll ausgegeben werden. Und wenn man sich die aufrufenden Stellen ansieht, ist klar dass es keine Rolle spielt, wenn das Register in der Funktion zuerstört wird. Deine chinesischen Zeichen müssen woanders herkommen.
temp1 ist doch der Eingabewert. Die Routine läuft 1A. Ob deine LCD- Ausgaberoutine richtig ist mag ich zu bezweifeln.
Leute, da ist wirklich ein Fehler drin :-o lcd_data erwartet das auszugebende Zeichen im temp1, und deshalb geht das schief, weil der 10er ja im temp2 gezählt wurde...
1 | : |
2 | ldi temp2, '0' |
3 | lcd_number_10: |
4 | subi temp1, 10 ; abzählen wieviele Zehner in |
5 | brcs lcd_number_1 ; der Zahl enthalten sind |
6 | inc temp2 ; Zehner mitzählen |
7 | rjmp lcd_number_10 |
8 | lcd_number_1: |
9 | rcall lcd_data ; die Zehnerstelle ausgeben |
10 | : |
Ich habe da mal einen schnellen Würgaround mit einem lokalen push/pop ins Tut gehackt, aber mir gefällt die Lösung von Peter Bednarz eigentlich besser... Und das könnte man noch ein wenig abkürzen:
1 | subi temp1, -10 ; 10 wieder dazuzählen, da die |
2 | ; vorhergehende Schleife 10 zuviel |
3 | ; abgezogen hat |
4 | ; das Subtrahieren von -10 |
5 | ; = Addition von +10 ist ein Trick |
6 | ; da kein addi Befehl existiert |
7 | ldi temp2, '0' ; die übrig gebliebenen Einer |
8 | add temp1, temp2 ; noch ausgeben |
9 | rcall lcd_data ; |
Denn -(-10) und +'0' zusammen ergeben 10+'0'
1 | ldi temp2, 10+'0' ; die übrig gebliebenen Einer |
2 | add temp1, temp2 ; korrigieren und ausgeben |
3 | rcall lcd_data ; |
Oder noch kürzer:
1 | subi temp1, -(10+'0') ; die übrig gebliebenen Einer korrigieren und ausgeben |
2 | rcall lcd_data ; |
Lothar Miller schrieb: > Leute, da ist wirklich ein Fehler drin :-o > lcd_data erwartet das auszugebende Zeichen im temp1, und deshalb geht > das schief, weil der 10er ja im temp2 gezählt wurde... Ah. Nach unten hin, zu den LCD Routinen, hab ichs nicht mehr kontrolliert. Ich hab mich auf dioe Aufrufe dieser Funktion konzentriert. > Ich habe da mal einen schnellen Würgaround mit einem lokalen push/pop > ins Tut gehackt, aber mir gefällt die Lösung von Peter Bednarz > eigentlich besser... Eigentlich sollte diese ganze temp-erei bereinigt werden und vernünftige Namen für die Register vergeben werden. Dann passiert so etwas nicht.
Das Problem ist, daß man sich als Assemblerprogrammierer erstmal Programmierkonventionen festlegen muß. Also welche Register sind für Argumente und Returnwerte reserviert und denen dann entsprechende Namen gibt. Und dann sollte man sich auch einige Scratchpadregister definieren, die niemals gepusht werden müssen. Wenn der Aufrufer Variablen später noch braucht, muß er sie selber sichern oder im SRAM ablegen. Und dann ist es ne gute Idee, für Interrupts Register zu reservieren, damit auch dort Push/Pop weitgehend überflüssig sind. Generell sollte man globale Variablen nicht in Registern halten. Register sollten nur die lokalen Arbeitspferde sein, sonst pusht/popt man sich nur tot. Gerade als Assemblerprogrammierer kann man nicht so einfach drauflos programmieren. Peter
Peter Dannegger schrieb: > Gerade als Assemblerprogrammierer kann man nicht so einfach drauflos > programmieren. Ich geb dir absolut recht. Das Problem im Tut ist, dass man irgendwo anfangen muss. Globale Variablen nicht in Register geht in den Kapiteln vor dem SRAM gar nicht. Auf der anderen Seite will man im Tut natürlich auch ein paar interessante Programme zeigen ohne lange drumherum reden zu müssen. D.h. die scheinbare Einfachheit der ersten Tut-Kapitel rächt sich, sobald es darum geht möglichst wiederverwendbare Module zu schaffen (wie zb LCD Ansteuerung). Zum Zeitpunkt, wenn die LCD Routinen eingeführt werden, möchte man im Tut natürlich nicht die großen, eigentlich notwendigen, Gedankengänge einführen und erläutern, die aber notwendig wären. Es ist halt schwer. Im Grunde läuft es immer wieder aufs Gleiche raus: Man müsste alles gleichzeitig vermitteln können:-) Mal sehen, ob man da mit klammheimlich eingeführten Registerkonventionen noch etwas retten kann. Im Tut wird ja r16 praktisch überall benutzt, in Ermangelung von vorausschauender Arbeitsweise, die auch berücksichtigt, was in späteren Kapiteln noch dazu kommt und welche Register dort benutzt werden.
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.