Forum: Mikrocontroller und Digitale Elektronik rechnen in Assembler mit AVR


von Matze0001 (Gast)


Lesenswert?

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

von Joe M. (nachdenklicher)


Lesenswert?


von spess53 (Gast)


Lesenswert?

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

von Jürgen W. (juergen_w) Benutzerseite


Lesenswert?


von Matze0001 (Gast)


Lesenswert?

Wow! Danke schon mal, ging ja flott!
Muss ich mich mal reindenken und ausprobieren...

Vielen lieben Dank!
Ihr seid die Besten!

von Matze0001 (Gast)


Lesenswert?

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

von jonas biensack (Gast)


Lesenswert?

kuck mal hier ;)

http://elm-chan.org/cc_e.html

Unter "AVR assembler libraries"

gruß jonas

von Joe M. (nachdenklicher)


Lesenswert?

> kuck mal hier ;)
> http://elm-chan.org/cc_e.html
> gruß jonas


Den 2.Beitrag zu diesem Thread schon gelesen??


Joe

von P. M. (o-o)


Lesenswert?

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!

von Steffen H. (avrsteffen)


Lesenswert?

>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?

von P. M. (o-o)


Lesenswert?

Ich hab leider keins, aber Google und die Stichwörter werden dir sicher 
weiterhelfen ;-)

von Matze0001 (Gast)


Lesenswert?

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

von Max G. (l0wside) Benutzerseite


Lesenswert?

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.

von Paul (Gast)


Lesenswert?

So wie man per Hand (mit Zettel und Stift, ohne Taschenrechner) 
dividiert.

von Jürgen W. (juergen_w) Benutzerseite


Lesenswert?

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

von Ralf (Gast)


Lesenswert?

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.

von Matze0001 (Gast)


Lesenswert?

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

von Jürgen W. (juergen_w) Benutzerseite


Lesenswert?

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.

von Matze0001 (Gast)


Lesenswert?

hab den code schon mal ausprobiert... nur was kommt da genau raus? Was 
sind das dann für Werte?

von Karl H. (kbuchegg)


Lesenswert?

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 :_)

von Sascha W. (sascha-w)


Lesenswert?

@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

von Matze0001 (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Matze0001 (Gast)


Lesenswert?

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? :)

von Karl H. (kbuchegg)


Lesenswert?

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."

von Matze0001 (Gast)


Lesenswert?

Ahhh so geht des!

JUHU! :) FREU

Ihr seid die besten! Vielen lieben Dank nochmal!
Ihr habt mir sehr geholfen!

Grüße, Matze

von Matze0001 (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

Spätestens jetzt ist es Zeit auf das
AVR-Tutorial
zu verweisen

von Thomas (kosmos)


Lesenswert?

es gibt ein Bit im Statusregister für negative Ergebnisse oder man 
vergleicht ob 87 größer als 100 ist.

von spess53 (Gast)


Lesenswert?

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

von Thomas (kosmos)


Lesenswert?

dann sorry für die Fehlinformation, musst es dann eben doch über einen 
vergleich lösen.

von spess53 (Gast)


Lesenswert?

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

von Thomas (kosmos)


Angehängte Dateien:

Lesenswert?

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.

von Matze0001 (Gast)


Lesenswert?

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

von spess53 (Gast)


Lesenswert?

Hi

>Ah ich habs!

Nicht wirklich.

brcs/brcc sind die brauchbaren Befehle.

MfG Spess

von Matze0001 (Gast)


Lesenswert?

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

von spess53 (Gast)


Lesenswert?

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

von Matze0001 (Gast)


Lesenswert?

Ah ok funktioniert auch!super! danke

muss einen auch erst mal einer sagen :)

von Matze0001 (Gast)


Lesenswert?

Hi, jetzt hab ich doch noch was :)

Temperaturanzeige funktioniert so weit...
jetzt wenn se aber ins minus geht stimmts nicht mehr... :(

von spess53 (Gast)


Lesenswert?

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

von Matze0001 (Gast)


Lesenswert?

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

von spess53 (Gast)


Lesenswert?

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

von Matze0001 (Gast)


Lesenswert?

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?

von spess53 (Gast)


Lesenswert?

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

von Matze0001 (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.