Hallo, ich habe ein kleines Programm für einen Atmega8 mit einem 2x16 LCD display von displaytech. Da der Speicher des uC bereits voll ist, möchte ich platz sparen. zum schreiben auf das Display benutze ich bisher diese Funktionen: void LCDEnable() { PORTD |= _BV(5); // sbi PORTD, 5 asm volatile("nop\n\tnop\n\tnop\n\t"::); asm volatile("nop\n\tnop\n\tnop\n\t"::); asm volatile("nop\n\tnop\n\tnop\n\t"::); asm volatile("nop\n\tnop\n\tnop\n\t"::); PORTD &= 0xff-_BV(5); // cbi PORTD, 5 } void LCDData(unsigned char data) { unsigned char data2 = data; asm volatile("swap %0\n\t" : "=r" (data) :); data &= 0x0F; data |= _BV(4); PORTD=data; LCDEnable(); data2 &= 0x0F; data2 |= _BV(4); PORTD=data2; LCDEnable(); Delay50us(); } dann gibt es noch eine funktion, welche die arrays line1 und line2 auf das diplay schreibt: void LCDPrint() { LCDGotoXY(1,1); LCDData(line1[0]); LCDData(line1[1]); LCDData(line1[2]); LCDData(line1[3]); LCDData(line1[4]); LCDData(line1[5]); LCDData(line1[6]); LCDData(line1[7]); LCDData(line1[8]); LCDData(line1[9]); LCDData(line1[10]); LCDData(line1[11]); LCDData(line1[12]); LCDData(line1[13]); LCDData(line1[14]); LCDData(line1[15]); LCDData(line1[16]); LCDData(line1[17]); LCDData(line1[18]); LCDData(line1[19]); LCDGotoXY(1,2); LCDData(line2[0]); LCDData(line2[1]); LCDData(line2[2]); LCDData(line2[3]); LCDData(line2[4]); LCDData(line2[5]); LCDData(line2[6]); LCDData(line2[7]); LCDData(line2[8]); LCDData(line2[9]); LCDData(line2[10]); LCDData(line2[11]); LCDData(line2[12]); LCDData(line2[13]); LCDData(line2[14]); LCDData(line2[15]); LCDData(line2[16]); LCDData(line2[17]); LCDData(line2[18]); LCDData(line2[19]); } LCDPrint ruft also für jedes Zeichen auf dem Display einmal LCDData auf und übergibt den char von der entsprechenden stelle im array. Da dies sehr umständlich und speicherraubend ist, wollte ich die ganzen lcddata dadurch ersetzen: for ( int i = 1 ; i < 17 ; i++ ) LCDData(line1[i]); komischer weise funktioniert das nicht. das erste zeichen wird noch richtig angezeigt, danach kommt immer das selbe. mit den einzelnen aufrufen von lcddata funktionierts tadellos! ich habe keine idee warum das nicht funktioniert mit der for schleife.. ich hoffe ihr könnt mir helfen! gruß nico
probiers mal so: unsigned char i; for (i = 0 ; i < 17 ; i++ ) LCDData(line1[i]);
Tja, Code sieht richtig aus (bis auf die unterschiedlichen Grenzen des Arrays -- der "Einzelcode" ist wohl von einem 2x20 übernommen? Und laß die for-Schleife von einem unsigned char durchzählen; das erkennt der Compiler vielleicht nicht alleine, daß keine int gebraucht wird). Bist Du Dir also sicher, daß Du den richtigen Inhalt in Deinem Array hast? "Funktioniert tadellos" ist ja schon erstaunlich, wenn Du offenbar 20 Zeichen in eine Zeile schreiben willst. Als unwahrscheinliche Möglichkeit gäbe es da noch ein Compilerproblem (avr-gcc?), z.B. beim Zusammenspiel von C und Assembler. Laß mal Spielchen wie das SWAP weg (nutzt das der Compiler nicht automatisch bei >>4 ?), um das auszuschließen. So etwas passiert gerne mit irgendwelchen fremdgestrickten delay50µs-Routinen, die dann gnadenlos ein Register belegen, das gar nicht frei ist. Und dann kannst Du den Spaß immer noch simulieren, um das Problem zu finden.
Das Problem liegt warscheinlich daran, dass du i erst in der Schleife deklarierst. Sowas funktioniert nicht bei jedem Compiler.
Alles (fast) falsch, was hier geschrieben wurde. Der Fehler liegt im asm-swap-Code. Richtig muss der heissen: asm volatile("swap %0\n\t" : "=r" (data) : "0" (data)); Der Constraint "=r" ist ein Write-Only-Output-Operand. Du musst dem Compiler durch den "0"-Input-Operand noch sagen, dass der Input im gleichen Register sein soll wie der erste Output-Operand. Die Schleife machst Du so, wie Benedikt geschrieben hat.
@unbekannter: von dem assembler verstehe ich nunmal garnichts, ich habe jetzt einfach nur die for schleife von benedikt übernommen und es klappt super! was würde es ändern, wenn ich die eine zeile bei LCDData durch deine Zeile ersetze? gruß nico
Naja, dass es nun funktioniert, ist reiner Zufall, weil der Compiler/Optimierer jetzt eben eine andere Registerbelegung verwendet. Sobald Du an anderer Stelle etwas veränderst, kann es sein dass es dann wieder nicht geht. Das Problem an der Fehlerhaften Zeile ist, dass der Compiler nur annimmt, dass diese Assembler-Zeile nur ein Ergebnis in einem Register liefert und der Compiler dieses Ergebnis in diesem Register im weiteren Code als 'data' verwenden soll. Der Compiler weiß überhaupt nicht, dass diese Assembler-Zeile schon vorher den Wert von 'data' im selben Register benötigt. D.h. der Compiler geht davon aus, dass in dem Register drinn stehen kann, was will weil die Assembler-Zeile darin eh etwas anderes reinschreibt. Dass es mit der geänderterten Schleife ('unsigned char' anstatt 'int') funktioniert, liegt also nur daran, dass das verwendete Register für 'unsigned char' der Schleife ein anderes ist, als die Regsiter für ein 'int' in der Schleife. Das Problem an diesen Assemblergeschichten ist immer, der Compiler "versteht" den Assembler-Code ja nicht. Der Compiler weiß nicht, was die Assembler-Zeile macht. Der Compiler muss aber wissen, was für ein Register die Assembler-Zeile benötigt (Typ 'r' ist jedes beliebige Register), und wo nach der Ausführung der Assembler-Zeile das Ergebnis steht. Und der Compiler muss auch wissen, in welches Register er den Eingabe-Parameter für die Assembler-Zeile stecken soll. Und das muss für den swap-Befehl eben genau im gleichen Register sein, wie für die Ausgabe verwendet werden soll. Du siehst also schon, diese Constraints sind die "Schnittstelle" zwischen Assembler-Code und C-Code. Und wenn die Schnittstelle falsch definiert ist, kannst Du Dir das so vorstellen, als ob Du eine C-Funktion mit falschen Parameter, z.B. ein Pointer anstatt ein Float, aufrufst. Die Funktion macht dann nur noch Mist. Genau so ist es bei der Assembler-Geschichte. Nur eben etwas gemeiner, weil der Compiler die Assembler-Zeile nicht überprüfen kann und sich zu 100% darauf verlassen muss, dass der Programmierer weiß, was er da tut. Und weiß der Programmierer eben nicht, was er da tut, kommen sehr fiese Fehler raus, weil die wirklich nicht eindeutig sind und von völlig anderen Sachen abhängen können. Also völlig unreproduzierbare Fehler. Du hast es ja selbst bemerkt: Nach Deiner Logik und auch nach dem Verständnis der anderen, hätte Dein Code funktionieren müssen. Es sind ja nur Vermutungen nach dem Motto "Versuch mal dieses, versuch mal jenes" gekommen, weil keiner eine Idee hatte, wo der Fehler liegt und jeder der festen Übezeugung war, dass Dein Code hätte eigentlich funktionieren müssen. Also, wenn Du keine unerklärlichen Fehler bei weiteren Programmänderungen haben willst, musst Du diese Zeile dringst ändern. Sonst taucht der Fehler irgendwann wieder plötzlich aus dem Nichts auf.
@ Nico: wenn Du gar nichts von Assembler verstehst, solltest Du die Finger davon lassen. (-; An dieser Stelle ist es -- wie schon von mir vermutet -- nicht nur gefährlich, sondern überflüssig, denn ich habe eben nachgeschaut: zumindest der avr-gcc benutzt ganz von alleine ein SWAP bei >>4.
>zumindest der avr-gcc benutzt ganz von alleine ein SWAP bei >>4.
Das stimmt nur teilweise:
Wenn man auf Codegröße optimiert wann wird eine Schleife mit mehreren
lsr/lsl/rol/ror daraus !
Mein Code ist auf Codegröße optimiert und ein SWAP mit ANDI ist ja auch kleiner als jede denkbare Schleife. Möglich, daß es Fälle gibt, in denen er für >>4 kein SWAP benutzt, aber mir fallen keine Fälle ein, wo es sinnvoll wäre. (-:
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.