Hallo! Ich hoffe ihr könnt mir ein wenig auf die Sprünge helfen. Also, ich lese die die 11 Bit breiten Daten des Digitalen Temperatursensors TsicXXX in meinen Mega8 ein. Diese liegen dann verteilt in 2 Registern sozusagen als Hexwert. Jetzt möchte ich die Temperatur aber mit einer 3-Stelligen 7-Segment-Anzeige ausgeben. (2 Stellen und eine nach den Komma) Da zu muss ich aber erst den Wert der in den 2 Registern liegt umrechnen: (Wert / 2047 * 200) - 50 Mal und Minus wären ja kein Problem nur das Dividieren... das Ergebnis soll dann in 3 Registern stehen: z.B.: Register 16 (Wert für 10er) Register 17 (Wert für 1er) Register 18 (Wert für Nachkommastelle) nur wie mache ich das in Assembler? Ich hoffe Ihr könnt mir da helfen Grüße, Matze
Hi Passende Routinen findest du hier: http://www.atmel.com/dyn/resources/prod_documents/doc0936.pdf http://www.atmel.com/dyn/resources/prod_documents/AVR200.zip Um die Nachkommastellen zu erhalten solltest du mit folgender Formel rechnen: ((Wert * 2000)/ 2047) - 500 Bei mir sieht das so aus (allerdings mit anderen Registern):
1 | ldi r22, Low(2000) ; x2000 |
2 | ldi r23,High(2000) |
3 | rcall mul16x16_32 |
4 | |
5 | ldi r20, Low(2047) |
6 | ldi r21,Byte2(2047) |
7 | ldi r22,Byte3(2047) |
8 | ldi r23,Byte4(2047) ; /2047 |
9 | rcall div32u |
10 | |
11 | movw XH:XL,r21:r20 |
12 | ldi r20, Low(500) |
13 | ldi r21,High(500) |
14 | sub XL,r20 |
15 | sbc XH,r21 ; -500 |
16 | clr r18 |
17 | |
18 | sbrs XH,7 ; Ergebnis negativ? |
19 | rjmp tsic80 ; wenn nicht |
20 | |
21 | ldi r17,1 ; 2er Complement |
22 | clr r18 |
23 | com XL |
24 | com XH |
25 | add XL,r17 |
26 | adc XH,r18 |
27 | |
28 | ldi r18,'-' ; Vorzeichen für Ausgabe |
29 | |
30 | tsic80: rcall HexToDec ; X nach Dezimal |
31 | .... |
Die andere Möglichkeit ist, statt durch 2047 durch 2048 zu teilen. Da brauchst du nur die unteren 11 Bit entfernen. Der Fehler ist minimal. MfG Spess
Wow! Danke schon mal, ging ja flott! Muss ich mich mal reindenken und ausprobieren... Vielen lieben Dank! Ihr seid die Besten!
Hi Spess! Wäre es möglich das du deinen ganzen Code postest? komm sonst ned wirklich klar :( bin noch ned so gut in Assembler Grüße Matze
> kuck mal hier ;) > http://elm-chan.org/cc_e.html > gruß jonas Den 2.Beitrag zu diesem Thread schon gelesen?? Joe
Tipp, falls es in den Links nicht schon erwähnt wurde: Dividieren und multiplizieren durch 2er-Potenzen macht man mit einem Shift! Divisonen durch Konstanten kann man auch als Fixkommamultiplikation mit dem Kehrwert implementieren!
>Tipp, falls es in den Links nicht schon erwähnt wurde: Dividieren und >multiplizieren durch 2er-Potenzen macht man mit einem Shift! Divisonen >durch Konstanten kann man auch als Fixkommamultiplikation mit dem >Kehrwert implementieren! Gibt es dafür Beispiele?
Ich hab leider keins, aber Google und die Stichwörter werden dir sicher weiterhelfen ;-)
Hi nochmal! Ich versuchs einfach nochmal hier... ich weis nimmer was ich noch machen soll... kriegs einfach ned hin... gibts denn irgendjemanden der mir den fertigen ASM-Code funktionsfähig geben kann oder jemanden der mir das in ASM Programmieren kann? Evtl. auch gegen eine kleine Aufwandsentschädigung... Grüße Matze
Matze0001 schrieb: > umrechnen: > (Wert / 2047 * 200) - 50 > das Ergebnis soll dann in 3 Registern stehen: > z.B.: > Register 16 (Wert für 10er) > Register 17 (Wert für 1er) > Register 18 (Wert für Nachkommastelle) Die einfachste Möglichkeit: bastle in Excel o.ä. eine Lookup-Tabelle und binde sie ins Programm ein. In der Tabelle stehen für jeden der möglichen Werte (ich nehme an: 0...2047) die zugehörigen Zehntel, Einer und Zehner. Mit etwas Nachdenken reicht für die Tabelle ein Byte je Eintrag (Zehntel und Einer), für Zehner und Vorzeichen musst Du dann eine zweite spendieren. Oder Du nimmst zwei Byte je Eintrag, dann ist es ganz einfach. Die zweiteinfachste Möglichkeit: Du probierst einfach alle Werte für die Anzeige vom kleinsten zum größten durch, so viele sind es ja nicht (2000 Stück, wenn ich mich nicht irre). Sobald der Anzeigewert größer ist als der gemessene, hast Du den richtigen Wert gefunden. Anders formuliert (Pseudocode): var zehner, einer, zehntel zehner = -5; einer = 0; zehntel = 0; loop: wenn (200 * messwert - 50 < 20470*zehner + 2047*einer + 205*zehntel) dann breche Schleife ab, Wert gefunden zehntel = zehntel + 1 einer = einer + 1 zehner = zehner + 1 end loop (Du musst natürlich drei Schleifen verschachteln und noch ein bisschen Aufwand für die negativen Zahlen treiben). Die Rechnerei dauert ein paar Millisekunden, aber das ist ja egal, so schnell kannst Du das Display eh nicht ablesen. Die komplizierteste Möglichkeit: vorwärts rechnen, wie von Dir vorgeschlagen. (Wert / 2047) zu rechnen ist diffizil, einfach ist: (Wert / 2048) = (Wert >> 11). >> ist der Rechtsshiftoperator, guck mal in Deinem C-Handbuch nach. In Assembler geht das natürlich auch. Bitte erst multiplizieren, dann den Rechtsshift machen, vorher überlegen, welche Wortbreite Du brauchst. Mit ((Wert * 25) >> 8) - 50 geht es übrigens auch. Wenn Du es andersherum machst, verlierst Du Genauigkeit. Anschließend musst Du das Ergebnis noch in Zehner, Einer und Zehntel zerlegen, das ist tendenziell die schwierigere Aufgabe.
So wie man per Hand (mit Zettel und Stift, ohne Taschenrechner) dividiert.
Hallo,
1 | |
2 | ;11Bit Rohwert des TSic in Temperatur umrechnen |
3 | ;Originalformel aus dem Datenblatt T= (Digital_signal/2047*(HT-LT)+LT) [°C] |
4 | ;LT = -50, HT = 150 als Standardwert für die Temperatur-Berechnung |
5 | ; |
6 | ;Umgestellt für ASM |
7 | ;t=TSic_Wert*200 /2048-50 (die 2047 um 1 erhöht um besser rechnen zu können) |
8 | ;t=TSic_Wert*25/256-50 (gekürzt /8) |
9 | ;Da eine Stelle hinter dem Komma angezeigt wird, rechne ich die Temperatur*10 und setze |
10 | ;vor die letzte Stelle den Dezimalpunkt (Festkomma). |
11 | ;Temperatur*10=(TSic_Wert*250)/256-500 |
12 | clr temp1 |
13 | mov temp2,tsicwert_low |
14 | mov temp3,tsicwert_high |
15 | lsl tsicwert_low ;*2 |
16 | rol tsicwert_high |
17 | add tsicwert_low,temp2 ;*3 |
18 | adc tsicwert_high,temp3 |
19 | lsl tsicwert_low ;*6 |
20 | rol tsicwert_high |
21 | sub temp1,tsicwert_low ;*256 - *6 = *250 |
22 | sbc temp2,tsicwert_high ;TSic_Wert*250 steht jetzt in temp1/temp2/temp3 |
23 | sbci temp3,0 ;Durch verwerfen des LSB (temp1) wird durch 256 geteilt |
24 | subi temp2,low(500) ;500 (50.0) abziehen |
25 | sbci temp3,high(500) |
26 | |
27 | ; Nach ASCii wandeln |
Routine läuft so einwandfrei bei mir Grüsse
Jürgen W. schrieb: > ;t=TSic_Wert*200 /2048-50 (die 2047 um 1 erhöht um besser rechnen zu können) Je nachdem, welcher Temperaturbereich gemessen werden soll, kann man auch 'TSic_Wert' noch um eins erhöhen, dann ist der max. Fehler (~0.1K) bei -50°C.
Danke für die ganzen Vorschläge! Echt super! Aber ich denke ich werde das mal mit der Tabelle ausprobieren... is halt ein "wenig" Schreibarbeit... :( Wie mache ich dann eine 3 Byte breite Tabelle? :) geht das überhaupt? Grüße Matze
Na dann mal viel Spass ;-) Bei 3 Bytes pro Tabelleneintrag sind das 6144 Bytes (6kb), Hoffentlich hast du dann noch genügend Platz für den Code. Was gefällt dir an meinem Code nicht ? Sind gerade mal 14 Befehle.
hab den code schon mal ausprobiert... nur was kommt da genau raus? Was sind das dann für Werte?
Matze0001 schrieb: > hab den code schon mal ausprobiert... nur was kommt da genau raus? Was > sind das dann für Werte? In temp3 das Highbyte des Ergebnisses, in temp2 das Lowbyte. Wobei der Wert das 10-fache der Temperatur ist. Bei 22 Grad kriegst du also 220 als Ergebnis, bei 38.7 Grad dementsprechend 387. Auf die Art hat man 1 'Kommastelle', obwohl man gar nicht mit Kommazahlen rechnet. Einfach während der Ausgabe auf einem LCD vor die letzte Stelle ein Komma 'einschmuggeln' und schon sieht es so aus als ob. Bei deinen 7-Segment LED-Anzeigen steht das Komma ja schon dort (auf der Frontplatte), da brauchst du also gar nichts tun :_)
@Matze0001 und wenn du jetzt 3 Werte einzel brauchst, dann musst du das Ergebnis noch mal durch 10 teilen, der Rest ist dann die Komma-Stelle. Das ganzzahlige Ergebnis teilst du wieder durch 10, womit der Rest dann die Einer-Stelle und das ganzzahlige Ergebnis die Zehner-Stelle ergibt. Sascha
ahh, jetzt klickts langsam! Vielen lieben dank euch! Wie kann ich euch ein Bier ausgeben? :) Ok und wenn ichs jetzt sozusagen durch 2047 teilen will damits genauer wird, dann wirds wieder zu kompliziert oder? (wär aber jetzt auch ned so wichtig bin froh das es jetzt so funktioniert) Und wie teil ich das jetzt noch durch 10? Und dann nochmal? damit ich die Zehner, Einer und die Nachkommastelle in einzelnen Registern hab? Wenn ihr mir da noch helfen könntet wärs perfekt Wär echt super sorry das ich mich da so anstelle.. Grüße Matze
Matze0001 schrieb: > ahh, jetzt klickts langsam! Vielen lieben dank euch! > > Wie kann ich euch ein Bier ausgeben? :) > > Ok und wenn ichs jetzt sozusagen durch 2047 teilen will damits genauer > wird, dann wirds wieder zu kompliziert oder? (wär aber jetzt auch ned so > wichtig bin froh das es jetzt so funktioniert) Ja wird es. Allerdings solltest du dir mal ausrechnen, wieviel denn dieses 'genauer' überhaupt ist. > Und wie teil ich das jetzt noch durch 10? Und dann nochmal? > damit ich die Zehner, Einer und die Nachkommastelle in einzelnen > Registern hab? zb indem man in einer Schleife 10 abzieht, solange bis die Zahl negativ wird. Gleichzeitig zählt man mit wie oft man abgezogen hat. Bei dir sind das natürlich erst mal die Hunderter. Du ziehst also in einer Schleife jeweils 100 ab und zählst mit oft das gut ging. Die Anzahl ist die Anzahl der Hunderter in der Zahl oder eben vulgo: das Ergebnis von Zahl dividiert durch 100. Zum Ergebnis (das ja jetzt negativ ist, schnell wieder 100 dazu, und das ganze Spielchen mit 10. Wie oft kann man 10 abziehen, ehe die Zahl negativ wird. Damit hast du die Zehner. Und das was übrig bleibt (10 dazu zählen um wieder positiv zu werden), sind dann die Einer. 387 +++ 0 387 - 100 = 287 1 287 - 100 = 187 2 187 - 100 = 87 3 87 - 100 = -13 Ooops | +------> 3 Hunderter -13 + 100 = 87 0 87 - 10 = 77 1 77 - 10 = 67 2 67 - 10 = 57 3 57 - 10 = 47 4 47 - 10 = 37 5 37 - 10 = 27 6 27 - 10 = 17 7 17 - 10 = 7 8 7 - 10 = -3 Oops | +------> 8 Zehner -3 + 10 = 7 -------> und 7 Einer das geht bei Zahlen in dieser Größenordnung schneller als richtige Divisionen. Und ist auch leichter implementiert.
ok, das bekomm ich hin! Vielen dank! Jetzt musst ich dann nur noch eins wissen... Wie kann ich jetzt die beiden Register mit 100 subtrahieren also 16bit - 8bit? :)
Matze0001 schrieb: > ok, das bekomm ich hin! Vielen dank! > Jetzt musst ich dann nur noch eins wissen... Wie kann ich jetzt die > beiden Register mit 100 subtrahieren > also 16bit - 8bit? :) Falsch 16 Bit - 16 Bit und das hast du im Code von Jürgen schon gesehen. Ich zitiere
1 | subi temp2,low(500) ;500 (50.0) abziehen |
2 | sbci temp3,high(500) |
:-) Code studieren! In jedem Code verbergen sich Schätze. Und nirgends ist das extensive Codestudium so wertvoll wie in Assembler. Bis man die Feinheiten von 5 oder 6 Anweisungen komplett in allen Details verstanden hat, das kann auch schon mal Stunden dauern. Aber dann lehnt man sich zurück und sagt: "Genial! Das hat was."
Ahhh so geht des! JUHU! :) FREU Ihr seid die besten! Vielen lieben Dank nochmal! Ihr habt mir sehr geholfen! Grüße, Matze
Jetzt hab ich doch noch was :/ ich subtrahiere und subtrahiere usw. und wie kann ich dann auswerten das die letzte Subtraktion nicht mehr in Ordnung war? Grüße Matze
es gibt ein Bit im Statusregister für negative Ergebnisse oder man vergleicht ob 87 größer als 100 ist.
Hi >es gibt ein Bit im Statusregister für negative Ergebnisse oder man >vergleicht ob 87 größer als 100 ist. Da würde ich vorsichtig sein. Das N-Flag representiert nur Bit 7 vom Ergebnis. $01-$02 -> $FF -> N=1 negativ $01-$A0 -> $61 -> N=0 positiv MfG Spess
dann sorry für die Fehlinformation, musst es dann eben doch über einen vergleich lösen.
Hi >dann sorry für die Fehlinformation, musst es dann eben doch über einen >vergleich lösen. Nein. Bei einem 'negativen Überlauf' wird das Carry-Flag gesetzt. Nur die Auswertung des N-Flags ist allgemein dafür nicht sinnvoll. MfG Spess
ja genau so wars, wusste noch das ich das mal über die Statusbits nach dem Abziehen gemacht habe. Matze001: Ansonsten einfach mal in der Befehlsübersicht nachschauen was nach dem subtrahieren passiert.
Ah ich habs! mit "brmi" am Ende der Subtraktion Wenn Temp3 ins Minus geht dann weiterspringen und zu temp2 100 dazu Danke euch allen nochmal Vielmals! Grüße Matze
Hi
>Ah ich habs!
Nicht wirklich.
brcs/brcc sind die brauchbaren Befehle.
MfG Spess
nur da ist doch das Problem, das eben das Carry-Flag bei der Subtraktion öffters mal gesetzt bzw. gelöscht ist bis die Berechnung fertig ist... und mit "brmi" wird nur weitergesprungen wenn das Ergebnis am Ende der Subtraktion negativ ist
Hi >nur da ist doch das Problem, das eben das Carry-Flag bei der Subtraktion >öffters mal gesetzt bzw. gelöscht ist bis die Berechnung fertig ist... Aber am Ende einer Rechnung stimmt es. Durch 'sbc'/'sbci' wird der Übertrag der vorherigen Rechnung einbezogen. MfG Spess
Ah ok funktioniert auch!super! danke muss einen auch erst mal einer sagen :)
Hi, jetzt hab ich doch noch was :) Temperaturanzeige funktioniert so weit... jetzt wenn se aber ins minus geht stimmts nicht mehr... :(
Hi
>jetzt wenn se aber ins minus geht stimmts nicht mehr... :(
Wieso? Ist halt eine negative Zahl. Um den Wert richtig darzustellen
musst du das 2er-Komplement bilden. Bei einem Byte geht da mit 'neg'.
Bei grösseren Zahlen so:
1 | ; 16 Bit in r17:r16 |
2 | |
3 | neg16: com r16 |
4 | com r17 |
5 | subi r16,Low(-1) |
6 | sbci r17,High(-1) |
7 | ret |
8 | |
9 | ;32 Bit in r19:r16 |
10 | |
11 | neg32: com r16 |
12 | com r17 |
13 | com r18 |
14 | com r19 |
15 | subi r16,Low(-1) |
16 | sbci r17,byte2(-1) |
17 | sbci r18,byte3(-1) |
18 | sbci r19,byte4(-1) |
19 | ret |
Damit kannst du dann normal weiter machen. Das war übrigens schon in meinem Beispiel enthalten Beitrag "Re: rechnen in Assembler mit AVR" MfG Spess
ja, aber ich rechne ja mit den Code von Jürgen W. mit deinen Code komm ich ned so richtig klar, weils immer nur Ausschnitte sind und nicht der ganze da ist dann wieder ein rcall irgendwo anders hin drin usw. kann ja ned hellsehen wo des dann hin geht... da komm ich ned mit klar... :( Sorry ned böse gmeint
Hi
>ja, aber ich rechne ja mit den Code von Jürgen W.
Na und. Da bekommst das Ergebnis in 2 Registern und von denen musst du
bei negativen Ergebnis das 2er-Komplement bilden. Die Routine 'neg16'
zeigt dir wie man das bei zwei Registern macht. Wenn du nicht mal in der
Lage bist, das auf deine Register umzusetzen tut es mir ehrlich leid.
MfG Spess
ok, jetzt komm ich klar damit :) vielen lieben Dank! sorry, programmier ja noch nicht so lange.. jetzt hab ich aber noch was... :) wie stell ich jetzt am besten fest das ich im Minusbereich bin damit ich dann den Sprung zum 2er-Komplement bilden machen kann?
Hi >wie stell ich jetzt am besten fest das ich im Minusbereich bin damit ich >dann den Sprung zum 2er-Komplement bilden machen kann? Bei einer negativen Zahl ist das höchstwertigste Bit gesetzt. Bei Operationen, die die Flags beeinflussen wird in diesem Fall das N-Flag (negative) gesetzt. Also hast du verschiedene Möglichkeiten: subi temp2,low(500) ;500 (50.0) abziehen sbci temp3,high(500) brmi/brpl .... tst temp3 brmi/brpl .... sbrc/sbrs temp3,7 rjmp .... MfG Spess
Ok Super! Funktioniert alles einwandfrei! Nochmal danke für eure Mühe und Geduld mit mir!! :) liebe Grüße, Matze
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.