Anbei ein einfaches Programm um einen 8bit Wert in ASCII umzuwandeln. Ist vielleicht nicht die eleganteste Lösung aber für meine Zwecke hat es bisher gereicht. Gruß Daniel Viel Spaß damit
Auf den ATMegas ist das so ziemlich die einfachste und schnellste Lösung. Gäbe es einen Divitionsbefehl ginge es jedoch einfacher. Vielleicht gibts noch eine obskure Möglichkeit das per Multiplikation zu machen.
Christian Berger wrote: > Vielleicht gibts noch eine obskure Möglichkeit das per Multiplikation zu > machen. Ja, die gibt es. Ich habe zwar momentan den Link nicht gefunden, aber irgendwo (ich glaube auf http://www.avrfreaks.net/ war das), gab es eine ziemlich ausführliche Diskussion dazu. Das Problem dabei ist nur, dass man eine Multiplikation mit einer relativ groeßn Zahl braucht, damit die Rundungsfehler oder ähnliches einem das Ergebnis nicht verfälschen.
Daniel Katzenberger wrote:
> Anbei ein einfaches Programm um einen 8bit Wert in ASCII umzuwandeln.
So gehts auch:
1 | ;input: R16 = 8 bit value 0 ... 255 |
2 | ;output: R18, R17, R16 = ASCII-digits |
3 | |
4 | bcd: |
5 | ldi r18, -1 + '0' |
6 | _bcd1: |
7 | inc r18 |
8 | subi r16, 100 |
9 | brcc _bcd1 |
10 | ldi r17, 10 + '0' |
11 | _bcd2: |
12 | dec r17 |
13 | subi r16, -10 |
14 | brcs _bcd2 |
15 | sbci r16, -'0' |
16 | ret |
Peter
Peter, könntest du deinen Code wohl noch etwas erläutern? Der ist schön kurz.
655432 wrote: > Peter, könntest du deinen Code wohl noch etwas erläutern? Der ist schön > kurz. Im Prinzip macht er das selbe, wie der Vorredner. Er beginnt bei einer Zahl in "Variable X". Ist X größer als 100, so zieht er 100 davon ab (X=X-100), und erhöht die 100-er Stelle. Dann kuckt er nach, ob der Rest (X) größer als 10 ist. Ist das der Fall, so zieht er 10 davon ab (X=X-10) und erhöht die 10-er Stelle. Im Pseudecode ist das etwa so: X: Originalzahl H: Hunderter Z: Zehner E: Einer H=0 Z=0 E=0 Solange X>100 X=X-100 H=H+1 Solange X>10 X=X-10 Z=Z+1 E=X Will man binär in BCD umwandeln, so kann man das für 2 Stellen etwas abkürzen: X: Originalzahl Y: BCD-Version Y=0 Solange X>10 X=X-10 Y=Y+$10 Y=Y+X
655432 wrote: > Peter, könntest du deinen Code wohl noch etwas erläutern? Der ist schön > kurz. Zuerst werden jeweils 100 abgezogen bis ein Unterlauf eintritt. Z.B. von 234 wird 3-mal abgezogen und übrig bleibt: 234 - 3 * 100 = -66. und -1 + '0' + 3 = '2' Dann wird 10 addiert, bis ein Überlauf eintritt: -66 + 7 * 10 = 4 und 10 + '0' - 7 = '3' Dann noch 4 + '0' = '4' und fertig. Peter
Hallo @ Peter Dannegger: dein code ist ja um einiges kürzer als meiner. Wär da nicht drauf gekommen... vorallem auf meinem at-mega habe ich die Register oberhalb von r16 schon alle in Verwendung, also ist mir nicht anderes übrig geblieben als die unteren Register zu verwenden... Trotzdem gut zu wissen, dass es noch einfacher geht. Gruß Daniel
Daniel Katzenberger wrote: > nicht drauf gekommen... vorallem auf meinem at-mega habe ich die > Register oberhalb von r16 schon alle in Verwendung, also ist mir nicht > anderes übrig geblieben als die unteren Register zu verwenden... Wenn Du länger programmierst, wirst Du merken, daß es unklug ist, alle Variablen ständig in Registern zu halten. Globale Variablen legt man überlicher Weise im SRAM an. Werden sie gebraucht, lädt man sie in Register, macht damit die anstehenden Aufgabe und schreibt die Ergebnisse wieder ins SRAM. Nur Variablen, die besonders häufig gebraucht werden, kann man ausnahmsweise in Registern definieren (vorzugsweise in den unteren R2..R15). D.h. die meisten Register sind Arbeitsregister und werden kurzzeitig verwendet, wie eine Wandtafel (Scratchpad-Register). Schau Dir mal größere Projekte von anderen an oder das Assemblerlistung von einem C-Programm. Peter
@ Peter Dannegger Ich arbeite schon ein paar Jahre mit µC allerdings mit PIC16FXXX. Auf Avr bin ich erst vor ein paar Wochen umgestiegen. Daher bin ich mit deren Struktur noch nicht so vertraut... Bei PIC kann man ja direkt mit werten rechnen die im SRAM stehen. Daher verwende ich den SRAM der AVR im moment nur für größere Datenmengen (Laufschrift für LCD usw.) Aber danke für die tipps. Ich hoffe ich kann mich "schnell" in die AVR-Welt einarbeiten. Gruß Daniel
Hallo Ich habe eine Lösung gefunden! Ist zwar etwas lang, aber es funktioniert! Einfach den gewünschten Wert in die Variable x speichern und du erhälst den Zehner und Einer Wert zurück. if((x>=0) && (x<=9)) { Ascii_Zehner=48; } if((x>=10) && (x<=19)) { Ascii_Zehner=49; } if((x>=20) && (x<=29)) { Ascii_Zehner=50; } if((x>=30) && (x<=39)) { Ascii_Zehner=51; } if((x>=40) && (x<=49)) { Ascii_Zehner=52; } if((x>=50) && (x<=59)) { Ascii_Zehner=53; } if((x>=60) && (x<=69)) { Ascii_Zehner=54; } if(x==0 || x==10 || x==20 || x==30 || x==40 || x==50 || x==60) { Ascii_Einer=48; } if(x==1 || x==11 || x==21 || x==31 || x==41 || x==51 || x==61) { Ascii_Einer=49; } if(x==2 || x==12 || x==22 || x==32 || x==42 || x==52 || x==62) { Ascii_Einer=50; } if(x==3 || x==13 || x==23 || x==33 || x==43 || x==53 || x==63) { Ascii_Einer=51; } if(x==4 || x==14 || x==24 || x==34 || x==44 || x==54) { Ascii_Einer=52; } if(x==5 || x==15 || x==25 || x==35 || x==45 || x==55) { Ascii_Einer=53; } if(x==6 || x==16 || x==26 || x==36 || x==46 || x==56) { Ascii_Einer=54; } if(x==7 || x==17 || x==27 || x==37 || x==47 || x==57) { Ascii_Einer=55; } if(x==8 || x==18 || x==28 || x==38 || x==48 || x==58) { Ascii_Einer=56; } if(x==9 || x==19 || x==29 || x==39 || x==49 || x==59 || x==69) { Ascii_Einer=57; }
Matthias Schläfli wrote: > Ich habe eine Lösung gefunden! Ist zwar etwas lang, Ja das stimmt, die ist wirklich schon extrem lang, 3800% größer als meine. > aber es > funktioniert! Aber nur von 0 ... 63. Was ist mit den Zahlen 64 ... 255? Peter
Hi @Matthias Kennt dein Compiler keinen 'mod' (Modulo) Befehl? Damit ist das mit ca. 10 Zeilen gegessen. MfG Spess
Qlternativ hier mal ein codeschnipsel den ich mir mal zusammen gebastelt habe, ist allerdings direkt aus ner LCD-Routine:
1 | void lcd_int8out(uint8_t i){ |
2 | bool temp = false; |
3 | if((i/100)||temp){lcd_data((i/100)+48);temp = true;}else{lcd_data(' ');}; |
4 | i %= 100; |
5 | if((i/10)||temp){lcd_data((i/10)+48);}else{lcd_data(' ');}; |
6 | i%= 10; |
7 | lcd_data(i+48); |
8 | };
|
Ist vermutlich am einfachsten zu verstehen, arbeitet allerdings mit Division ;)
Marc Seiffert wrote: > Ist vermutlich am einfachsten zu verstehen, arbeitet allerdings mit > Division ;) Alles in möglichst wenig Code Zeilen zu stecken fördert aber nicht die Verstehbarkeit ;)
Simon K. wrote: > Alles in möglichst wenig Code Zeilen zu stecken fördert aber nicht die > Verstehbarkeit ;) Das stimmt. Man kann ein Programm auch als Einzeiler schreiben, aber dann muß der Code noch lange nicht kleiner sein, als mit 1000 Zeilen. Besonders der AVR-GCC läßt sich mit verschiedenen Schreibweisen überhaupt nicht beeinflussen, der compiliert (leider) immer seinen eigenen Stil. Man sollte aber nicht mit der Brechstange versuchen, alles nur mit Vergleichen zu machen. Man kann ja auch jede beliebige Digitalschaltng nur mit 2-fach NAND (74HC00) aufbauen, aber das macht keiner. Und es schadet nichts, zu wissen, daß 10 Subtraktionen effizienter sind als eine Division auf MCs ohne Hardware-Division. Peter
>Ja das stimmt, die ist wirklich schon extrem lang, 3800% größer als >meine. Welche Codesammlung muß man eigentlich im Kopf haben, um so krankhaft angeben zu müssen?
Peter D. schrieb: > Daniel Katzenberger wrote: >> Anbei ein einfaches Programm um einen 8bit Wert in ASCII umzuwandeln. > > So gehts auch: >
1 | > |
2 | > ;input: R16 = 8 bit value 0 ... 255 |
3 | > ;output: R18, R17, R16 = ASCII-digits |
4 | > |
5 | > bcd: |
6 | > ldi r18, -1 + '0' |
7 | > _bcd1: |
8 | > inc r18 |
9 | > subi r16, 100 |
10 | > brcc _bcd1 |
11 | > ldi r17, 10 + '0' |
12 | > _bcd2: |
13 | > dec r17 |
14 | > subi r16, -10 |
15 | > brcs _bcd2 |
16 | > sbci r16, -'0' |
17 | > ret |
18 | > |
19 | > |
> > Peter Habe heute so eine Routine gebraucht. Ich bin ja nicht so sattelfest, aber ich finde, dass da drin ein Fehler steckt. Nämlich entweder die 1. Zeile muss sein
1 | ldi r18, -2 + '0' |
2 | |
3 | ;oder man fügt nach der Zeile mit brcc eine Zeile |
4 | |
5 | dec r18 ;ein |
Später im Thread mit dem Beispiel 234 sehe ich nirgends -1 +'0', die die 3 Hunderter auf 2 Hunderter korrigiert. Man darf ja nicht das erste inc r18 vergessen. Wo liegt mein ev. Fehler?
Rudi D. schrieb: die die 3 Hunderter auf 2 Hunderter korrigiert. Man darf ja nicht das > erste inc r18 vergessen. Dem ersten inc r18 wird doch dadurch Rechnung getragen, dass er nicht bei 0 anfängt zu zählen, sondern bei -1. Sind 3 Subtraktionen notwendig, bis das Ergebnis unterläuft (salopp gesprochen: negativ wird), dann waren genau 2 Hunderter in der Zahl. Und genau das kriegt er doch auch raus. Einfach mal im Einzelschrittbetrieb durchsteppen. Dann sollte das Prinzip klarer werden.
:
Bearbeitet durch User
Da sieht man was im Kopf falsch laufen kann. Die 3. Subtraktion verursacht ja kein inc r18 mehr. Aber am Papier hab ich das getan, als ich es durchspielte. Danke Rudi
Rudi D. schrieb: > Da sieht man was im Kopf falsch laufen kann. Die 3. Subtraktion > verursacht ja kein inc r18 mehr. Aber am Papier hab ich das getan, als > ich es durchspielte. > Danke Rudi Das Codeschnipsel wird im DDS-Generator bei der Einstellung der Wobbelfrequenz von 2 Hz bis 73 Hz erfolgreich verwendet Siehe http://www.radiomuseum.org/forum/neues_vom_dds_heimsenderlein.html Der Thread dort ist schon sehr lang.
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.