Hiho, Leider hab ich nach längerem suchen herausgefunden, das es nicht möglich ist ein "unsigned long long" mit sprintf/printf in ein Char Array zu schreiben um dieses auf der UART auszugeben. Nun hab ich dies durch folgenden C Code realisiert: void uart_ulltoa(unsigned long long val) { char Buffer[50]; char tmp; int i=0; while(val > 0) { Buffer[i++]='0'+val%10; val/=10; } for(int j = 0; j < i / 2; ++j ) { tmp = Buffer[j]; Buffer[j] = Buffer[i-j-1]; Buffer[i-j-1] = tmp; } Buffer[i] = 0; uart_puts(Buffer); } Funktioniert auch sehr gut soweit, das Problem dabei ist nur das die Funktion 8kbyte(!) Flash sowie ~250byte Ram benötigt. Das problem liegt in folgender Zeile: val/=10; Gibt es eine Möglichkeit dies nun noch auf "speicher" zu optimieren? CPU last wäre mir realitiv egal. Vielen Dank Blonkel
blonkel schrieb: > sowie ~250byte Ram benötigt. Genauer gesagt: 256 Bytes. Die kommen aus einer Bibliotheksfunktion, sie werden für das Array __clz_tab[] belegt. Wenn man danach gugelt, findet man, dass die Gleitkommabibliothek das zwar für die Gleitkommadivision ausgeräumt hat, aber offenbar schleppt's der GCC beim (wenig optimierten) long-long-Code dann wieder mit ein. Du müsstest wohl die Funktion __udivdi3() reimplementieren, um das loszuwerden. p.s.: "Bitte den Controllertyp im Titel mit angeben."
Wenn du sonst keinerlei Division mit "unsigned long long" in deinem Code brauchst, gibt es einen Weg, die Division (und damit die Einbindung der Routinen/Tabellen) komplett zu vermeiden. Dazu ist ein kleiner Umweg über die Packed-BCD-Darstellung notwendig. Gebraucht werden dann (auf AVR) nur die Register plus ein wenig Stack zum Sichern der benutzten Register. Zunächst wird dazu die Zahl komplett in Packed BCD umgewandelt. Dazu musst du den Algorithmus zu bin2BCD16 aus folgender Appnote verwenden: http://www.atmel.com/dyn/resources/prod_documents/doc0938.pdf Die Erweiterung von 16 Bit Input, 3 Byte Output auf 64 Bit Input, 10 Byte Output (2^64-1 hat 20 Dezimalstellen, also 10 BCD-Bytes) ist ziemlich "straight forward". Sinnvoll implementieren lässt sich der Algorithmus IMO eigentlich nur in Assembler, da dabei fast nur auf Bitebene mit Einbeziehung von Carry gearbeitet wird. Wenn ich mich beim Überschlagen gerade nicht grob vertan habe, dürfte dieser Part dann so um die 10000 Takte plus/minus brauchen. Wenn es nicht auf das letzte Code-Byte ankommt, würde ich die in der Appnote im Flussdiagramm dargestellte innere Schleife (das untere Drittel) übrigens nicht als Schleife schreiben, sondern komplett ausrollen (die Zahl der Durchläufe ist immer konstant). Nur so können auch die kompletten Outputbytes in Registern gehalten werden. Anschliessend muss dann noch von BCD nach ASCII umgewandelt werden, das braucht nur noch Byte-Division bzw. -Modulo mit 16, also per Bitoperationen zu erledigen. Hierbei könnte es zweckmässig sein, die generierten Zeichen einfach direkt in den UART zu schieben, anstatt erst einen String zu schreiben und den dann an uart_puts() zu übergeben. Andreas
Jörg Wunsch schrieb: > ...aber offenbar schleppt's der GCC > beim (wenig optimierten) long-long-Code dann wieder mit ein... Wenig optimiert bezieht sich hier wohl schätz ich nicht auf meine Optimierungstufe sondern auf das verabeiten von ull's von gcc? > p.s.: "Bitte den Controllertyp im Titel mit angeben." Tut mir leid, es handelt sich um hierbei um einen AVR atmega162. Andreas Ferber schrieb: > Sinnvoll implementieren lässt sich der Algorithmus IMO eigentlich nur in > Assembler, da dabei fast nur auf Bitebene mit Einbeziehung von Carry > gearbeitet wird. Wenn ich mich beim Überschlagen gerade nicht grob > vertan habe, dürfte dieser Part dann so um die 10000 Takte plus/minus > brauchen. Vielen Dank für die vorgeschlagene Lösung, einziger Nachteil dabei ist das meine Assembler Kenntnisse ein wenig zurück liegen. Sollte es dennoch keine Alternative geben, werde ich wohl den oben genannten Algorithmus implementieren.
blonkel schrieb: > Jörg Wunsch schrieb: >> ...aber offenbar schleppt's der GCC >> beim (wenig optimierten) long-long-Code dann wieder mit ein... > Wenig optimiert bezieht sich hier wohl schätz ich nicht auf meine > Optimierungstufe sondern auf das verabeiten von ull's von gcc? Ja, auf die interne Bibliothek (libgcc.a). Deren 64-bit-Code ist einfach stur maschinengeneriert, während viele andere Dinge (32-bit integer und floating-point) handoptimierten oder manuell nachgebesserten Code benutzen.
blonkel schrieb: > dennoch keine Alternative geben, werde ich wohl den oben genannten > Algorithmus implementieren. Die unsigned long long überhaupt loszuwerden, geht nicht? Was ist mit Subtraktionen, brauchen die weniger?
Karl heinz Buchegger schrieb: > Was ist mit Subtraktionen, brauchen die weniger? Ich denke schon, Addition und Subtraktion lassen sich ja relativ geradlinig implementieren. Dieses __clz_bits[] ist irgendwie ein Array, das das Zählen der führenden Nullen ("count leading zeros") vereinfachen soll, wobei halt die Implementierer wohl davon ausgegangen waren, dass das Opfern von 256 Bytes RAM für den erzielten Geschwindigkeitsgewinn (auf einem PC) "Peanuts" sind. Dummerweise gibt's offenbar halt keine Möglichkeit, das pro Target umzuschalten, und erst recht fehlt die Infrastruktur im GCC, um für verschiedenen -Os-Stufen verschiedene libgcc's zu linken, die ihrerseits wiederum selbst der jeweiligen Optimierungsstufe gerecht werden.
Jörg Wunsch schrieb: > Karl heinz Buchegger schrieb: > >> Was ist mit Subtraktionen, brauchen die weniger? > > Ich denke schon, Addition und Subtraktion lassen sich ja relativ > geradlinig implementieren. Schön ist es nicht unbedingt aber zyklenmässig würde man wahrscheinlich mit fortgesetzter Subtraktion runterbringen können. Problem ist natürlich, dass ein ull schon sehr hoch reicht und man viele einzelne Subtraktionsstufen braucht.
Jörg Wunsch schrieb: > und erst recht fehlt die > Infrastruktur im GCC, um für verschiedenen -Os-Stufen verschiedene > libgcc's zu linken Doch, das geht eigentlich schon, über Änderungen/Ergänzungen der specs. Der "*libgcc"-Spec-String ist speziell dazu vorgesehen, beim Linken die passende libgcc-Variante auszuwählen. Beim avr-gcc wird damit z.B. dafür gesorgt, dass bei den ganz kleinen ATtiny (11, 12, 15, 28) keine libgcc gelinkt wird. "Verzweigung" anhand der gewählten "-O"-Einstellung lässt die Spec-Syntax dabei auch zu, z.B. so:
1 | *libgcc: |
2 | %{!mmcu=at90s1*:%{!mmcu=attiny11:%{!mmcu=attiny12:%{!mmcu=attiny15:%{!mmcu=attiny28: %{Os|O0:-lgcc_small; O*:-lgcc_fast; :-lgcc_small }}}}}} |
Voraussetzung ist natürlich, dass beim Linken dann auch "-O..." mit übergeben wird, was normalerweise eigentlich nicht unbedingt nötig ist, aber auch nicht schadet und oft sowieso im Makefile gemacht wird. Hilft aber alles nichts, solange sich keiner findet, der entsprechend auf Größe optimierte Funktionen schreibt ;-) Andreas
Für alle die das Problem auch irgendwann haben hier meine Lösung (Ansatz dafür war irgendwo im Netz, leider hab ich die Quelle nicht mehr): void bin2bcd64(unsigned long long inValue) { unsigned char acc[21]={0}; unsigned char carry[22]={0}; for (int loop=0;loop<64;loop++) { if (( inValue & 0x8000000000000000) != 0) carry[0] = 1; else carry[0] =0; inValue <<= 1; /* If carry generated to next digit, convert and add carry. If no carry generated to next digit, shift left 1 step and add carry. */ for(int i=1; i < 21;i++) { if (carry[i] == 1) acc[i-1] = ((acc[i-1]-5)*2) + carry[i-1]; else acc[i-1] = (acc[i-1] << 1) + carry[i-1]; } // Check each accumulator if >= 5. Generate carry if so. for(int i=0; i < 21;i++) { if (acc[i] >= 5) carry[i+1] = 1; else carry[i+1] = 0; } } //Output without leeding zeros char zero=1; for(int i=19; i >= 0;i--) { if((zero == 1 && acc[i] != 0) || i == 0) { zero=0; } if(zero == 0) { uart1_putchar('0' + acc[i]); } } }
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.