rechnen in Assembler mit AVR

Gast #2164431
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
Gast #2164489
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
Gast #2186873
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
Persönliche Seite #2187263
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.
Persönliche Seite #2187770
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
Gast #2187855
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
#2187898
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 :_)
#2187910
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
Gast #2188332
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
#2188343
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.
#2188376
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."
Gast #2188443
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
Gast #2188488
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
Gast #2188779
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
Gast #2188784
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
Gast #2190903
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
Gast #2190930
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
Gast #2191026
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
Gast #2191407
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?
Gast #2191437
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren