www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik 1023 auf 100% umformen


Autor: Fabian Knapp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,
habe ein kleines Problem:
Ich ermittle über die ADC's verschiedene Spannungen, diese werden dann
als 10bit variable gespeichert. Nun will ich diese 10bit in 0-100%
umformen.
Wenn ich jetzt einfach den wert solange um 10 subtrahiere, bis das
carrybit nen wert enthält habe ich Abweichungen um bis zu 2,4%. Diese
würde ich gerne vermeiden! Hat da jmd. ne Lösung parat? Bin bestimmt
nicht der Erste der das Problem hat.

Vielen Dank im Vorraus

Autor: Philipp Burch (philipp_burch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Prozent = Wert / 1024 * 100
um die Division zu vermeiden kannst du erstmal
x = Wert * 100
rechnen und dann das Ergebnis (16 Bit) um 10 Bit nach recht's schieben
was einer Division durch 1024 entspricht.

Autor: Fabian Knapp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wenn du mir jetzt noch sagst wie ich im mega8 mit 100 multiplitzieren
kann bin ich dir sehr dankbar :D

Autor: ulf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
100 * x = 64 * x + 32 * x + 4 * x

lässt sich also mit shiften und addieren durchführen

Autor: ulf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
allerdings könnte die division durch 1024 probleme machen: der maximale
wert, den der ad-wandler liefert ist ja 1023, d.h. wenn man den
prozentwert mit

prozentwert = ad_wert * 100 / 1024

berechnet, kommen maximal etwa 99.90% raus, was bei integer-rechnungen
zu 99% abgeschnitten würde. um die 0..1023 des ad-wandlers wirklich auf
die 0..100% abzubilden müsstest du also durch 1023 dividieren. das
könnte dann wieder ein bisschen aufwändiger werden.

Autor: Philipp Burch (philipp_burch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zu oben genannter Rechnung: Geht noch etwas einfacher (Diesmal mit dem
Hardware-Multiplizierer):
x = HIGH(ADCH * 100)
Damit multiplizierst du erstmal das High-Byte des ADCs (Ergebnis
unbedingt linksbündig ausrichten lassen) mit 100 und nimmst davon
wiederum nur das High-Byte des Ergebnisses. Dort steht dann dein Wert
von 0 bis 99% drin. Um jetzt auf deine 100% zu kommen, kannst du den
Wert noch entsprechend runden lassen:
Wenn das MSB von ADCL gesetzt ist, dann addiere zum Endergebnis 1
dazu.
Das ganze in AVR ASM (ungetestet):
in r16, ADCH
ldi r17, 100
mul r16, r17
;Ergebnis ist nun in r1:r0, du brauchst aber nur r1
in r16, ADCL
andi r16, 0x7F
breq PC + 2
    inc r0
;Nun hast du dein gerundetes Ergebnis von 0 - 100% in r0
Funktion ohne Gewähr, müsste aber so ungefähr funzen. Bin mir nicht
mehr ganz sicher, wie die Registernamen vom ADC wirklich heissen.

Autor: Hagen Re (hagen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
nicht zum Endergebnis +1 addieren sondern auf den ADC_Wert. Ein ADC_Wert
von 0 oder 1 wird immer 0% ergeben.

Gruß Hagen

Autor: Fabian Knapp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
also ich bezweifle das das geht... habe es auch mal im simulator
durchlaufen lassen und wenn ich für ADCH 0b00000011 nehme und für ADCL
0b11111111 müssten ja 100 rauskommen. Stattdessen liegt der wert unter
50 und bei anderen werten wo normalerweise 70 % rauskommen müsste
erhalt ich zahlen über 200

Autor: Johannes A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Setze mal das ADLAR Bit, dann erhälst Du als max Wert
ADCH = 0b11111111 und ADCL = 0b11000000.
Das ist es, was Phillip mit "linksbündig" meinte.

Sonst musst Du eine vollständige 16x8bit Multiplikation
durchführen, und ein Ergebnis von 24bit verwursten.

Autor: Philipp Burch (philipp_burch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich schrieb ja, dass du das Wandler-Ergebnis linksbündig ausrichten
musst (ADLAR = 1). Dann hast du in ADCH einen Wert von 0 bis 255, nicht
von 0 bis 3.

@Hagen:

??? Es geht doch darum, dass er 100% erreichen kann. Dieses +1 ist
eigentlich nur die "Rundung".
Beispiel:
ADCH = 127; ADCL = 0
x = (127 * 100) >> 8 = 49
ADCH = 127; ADCL = 255
x = (127 * 100) >> 8 + 1 = 50
ADCH = 255; ADCL = 0
x = (255 * 100) >> 8 = 99
ADCH = 255; ADCL = 255
x = (255 * 100) >> 8 + 1 = 100

Wo ist das Problem?

Autor: Fabian Knapp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So sorry habe das Linksbündig doch glatt überlesen...
Also jetzt geht es fast...
wie Hagen schon gesagt hat ist das Problem, dass ein ADC Wert von 1
nach deiner Rechnung 1 % ergibt, obwohl es nur 0,09 % sind und deshalb
nicht aufgerundet werden dürfte. Ist ansich nicht schlimm, kommt aber
nen bisschen blöd, wenn auf dem display 1% angezeigt wird, obwohl der
Poti am Anschlag ist.

Autor: Hagen Re (hagen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das "Problem" ist ein anderes. Die Korrektur um einen Wert von +1
erzeugt ja in jedem Falle einen "Fehler". Wird der ADC Wert vor der
Berechnung um +1 "aufgerundet" so beträgt dieser Fehler exakt
1/1024'tel. Wird nach der Rechnung der Prozentwert um +1 erhöht dann
beträgt der Fehler aber schon 1024/100'tel = 10.24 statt nur +1.

Gruß Hagen

Autor: Johannes A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das "Problem" ist tatsächlich folgendes:

Die +1 für die Rundung einer Ganzzahldivision müssen vom Divisor
abgeleitet werden, und nicht vom Dividenden.

Als Formel ausgedrückt:

x = (a + b/2) / b

Das heißt hier, sorry Philipp, die Rundung direkt vom ADC-Wert
abzuleiten, funktioniert nicht in allen Fällen zuverlässig.

Ich kuck mir die Sache nochmal an und poste über den Abend noch einen
konkreten Code. Sofern es nicht jemand anders tut ;-)

Grüße
Johannes

Autor: Hagen Re (hagen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Naja, es geht ja nicht um "Rundung" eines Wertes, noch um eine
Scalierung eines Wertes, sondern um eine Transposition um +1, einfach
um als Resultat bei 1023/1024*100 nicht 99% zu bekommen, sondern 100%.

Korrekterweise müsste man nämlich durch 1023 statt 1024 dividieren,
aber aus Effizienzgründen nimmt man 1024. Das verursacht quasi einen
Fehler von exakt +1 und den gleichen wir einfach aus. Da das Resultat
in seiner Auflösung als Prozentzahl eh einen Fehler von 1024/100 =
10.24 hat stört also die Addition von +1 auf den ADC Wert garnicht.

Gruß Hagen

Autor: Philipp Burch (philipp_burch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ach ja, jetzt komm' ich nach. Ihr habt natürlich recht, tut mir leid.

Autor: Johannes A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hagen, mit der Scalierung hast Du im Prinzip Recht, bloß sind die 100%
normal gar nicht das Hauptproblem, sondern die 0%. Die 100% kannst Du
zur Not noch "zurechtlügen", das nimmt Dir keiner krumm. Nur wenn
eine Anzeige bei 0% irgendwas anderes anzeigt, mault Dir gleich jeder
die Ohren voll. Und das wird schon bei nur 3 Kunden echt nervig ;-)

Gruß Johannes

Autor: Profi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Genau das habe ich letzte Woche gemacht, und zwar für 3,29678, 3,3000,
4,99512 5,000V  99,902 und 100%.

Und zwar folgendermaßen:
zuerst den ADCmit 0x20 multiplizieren (also um 5 Bits nach links
schieben), dann ein paar Bytes 0x00 anhängen.
Davon zuerst den ADC-Wert für 100% abziehen (mit
Floating-Nachkommastellen), sooft, bis das Ergebnis negativ wird, dann
den letzten Schritt mit einer Addition rückgängig gemacht und das selbe
mit dem ADC-Wert für 10% , 1%, usf.
Inspiriert hat mich die Routine für die Integer -> LCD-Ausgabe.
Nur dass ich nicht 1000, 100, 10 oder 1 subtrahiere, sondern den
ADC-Wert für die jeweiligen Stellen.

Code hab ich gerade nicht auf diesem Rechner, kann ich in den nächsten
Tagen posten.
Nur soviel, falls Du es selbst versuchen willst:
10 Bits sind max. 1023=0x3ff  Hier die Version für 99,901%:
mal 0x20 = 0x7fe0, daran beliebig viel Nullen angehängt:
7fe00000   davon den Wert für 10% abziehen:
0ccccccd   sooft, bis das Ergebnis negativ wird, das geht 10 Mal
...            von 10 ziehst Du 1 ab und erhälts die erste Ziffer 9
ffdffffe   negatives Ergebnis, dazu einmal addieren:
 caccccb
 und nun geht es von vorne los: den ADC-Wert für 1% subtrahieren
 147ae14    (0x80000000 / 100)
.... geht wieder 10 mal, also 2. Ziffer wieder 9, dann Komma einfügen
ffe00003  wieder 147ae14 addieren
=127ae17  (0x80000000 / 1000)
- 20c49b  subtrahieren, geht wieder 10 Mal -> 3. Ziffer auch 9
ffe00009
+ 20c49b
=   c4a4
-  346DC  0x80000000 / 10000)
fffd7dc8  wird gleich beim erstem Mal negativ -> 4. Ziffer 0
falls mehr Stellen gewünscht, weitermachen

wenn Du bei 0x3ff 100,0 anzeigen willst, musst Du 0x7fe00000 / 10 ...
rechnen.

Autor: Ralf Kellerbauer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
10 Bit genau messen und auf unter 7 Bit wandeln ?

Zunächst reichen 8 statt 10 Bit für den gelesenen Wert, also Wandler
auf 8 Bit stellen oder per 16 Bit 'schieben' auf 8 Bit vermindern.

Also haben wir 0...255 als mögliche Werte, welche man einmal mit 100
per Hardware multipliziert.
In R0/R1 haben wir dann ein high-Byte zwischen 0...99 und ein low Byte.

Ist Bit 7 im low Byte = high wird das High-Byte per INC um +1 erhöht.

Der Bereich im High-Byte liegt dann bei 0...100%.

---
Will man 10 Bit / 0,0% bis 100,0% muss man die Analgogwert (0...1023)
mit (binär) 1001 multiplizieren.
Das Ergebis dann /1024 = shiftr 12 bzw. shiftr 4 und Ergebnisregister
neu bezeichen.
Ergebnis dann 0 bis 100,0%

Autor: Ralf Kellerbauer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Korrektur:

Bei 8 Bit Auflösung auch nur mit '101' multiplizieren und das low
Byte der Multiplikation vergessen.

Autor: Hagen Re (hagen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"Ist Bit 7 im low Byte = high wird das High-Byte per INC um +1
erhöht."

Ich würde diesen 16 Bit Wert immer mit 0x0080 addieren, spart den
begingten Sprung.

Gruß Hagen

Autor: Fabian Knapp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
vielen Dank für die große Hilfe.
Werde morgen Ralfs version ausprobieren und die addition mit 0x80
erscheint mir auch als logisch.

Autor: Johannes A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hatte einen konkreten Code versprochen, hier ist er nun.
Er ist ziemlich genau, wahrscheinlich genauer als gebraucht,
aber sei es drum. Vielleicht hilft es ja noch jemandem.

Also erstmal ADLAR wieder auf 0 und nachdem der ADC fertig ist,
das Ergebnis einlesen:

  in   r16,ADCL
  in   r17,ADCH

Danach 1 addieren bzw. -1 (=0xFFFF) subtrahieren, um die 100%
sicher zu erreichen:

  subi r16,0xFF
  sbci r17,0xFF

Dann mit 100 multiplizieren, wobei ein Wert >16bit
herauskommen kann (R2-R4):

  ldi  r18,100
  mul  r16, r18      ; adcl * 100
  movw r2, r0
  clr  r4
  mul  r17, r18      ; adch * 100
  add  r3, r0
  adc  r4, r1

Nun teile ich durch Verschieben durch 256...

  mov  r16,r3        ; r4:r3:r2 / 256
  mov  r17,r4

...addiere den entsprechenden Wert für die Rundung...

  subi r16,0xFE      ; + (512/256) entspr. -2
  sbci r17,0xFF

...und teile per Rechtschieben noch einmal durch 4:

  lsr  r17           ; / (1024/256)
  ror  r16           ; entspr. r17:r16>>2
  lsr  r17
  ror  r16

Ist doch eigentlich gar nicht so kompliziert. Oder?

Beste Grüße
Johannes

Autor: Profi (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
So, hier meine ultra-genaue Version zum Konvertieren einer n-bittigen in
eine beliebige Zahl. Beispiele für 3,3, 5,0 100% sind dabei.

Die Routine macht beide Aufgaben auf einen Streich:
- Umwandeln in den passende Zahlenbereich  und
- Aufbereiten der Ausgabe in einzelne Ziffern.

Viel Spaß und Erfolg

Autor: Andreas Schwarz (andreas) (Admin) Benutzerseite Flattr this
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Diskussion 1023 vs. 1024 hatten wir doch vor ein paar Tagen
schonmal:
http://www.mikrocontroller.net/forum/read-1-428358...

Autor: Fabian Knapp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sorry @andreas werde in zukunft die suchfunktion zuerst nutzen.

Vielen Dank für die Programme ihr Freax :P

Autor: Vor- und Nachname (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
...
If you've just spent nearly 30 hours
Debugging some assembly,
Soon you will be glad to
Write in C.
...

Autor: Fabian Knapp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also Johannes habe jetzt dein programm mal ausprobiert und es kommt bei
mir nur blödsinn raus, das einzigste was richtig ist ist 0 und 100
prozent

Autor: Fabian Knapp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Großes Entschuldigung. war doch tatsächlich fähig das High und low byte
für nen test zu vertauschen! Kopf gegen die Wand hau
Dein Code funzt echt super!!!

Vielen Vielen Dank!

Autor: Johannes A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Fabian
Na prima!

@Andreas
Nichts für ungut, aber float und int sind doch zwei verschiedene
Welten. Auch wenn man sich in beiden über dasselbe Thema unterhält.

Und dann noch an diesen namenlosen Witzbold
...
Beleive in C
And you will find,
That you must beleive,
That world is disc,
Too.
...

Grüße an alle,
hat Spaß gemacht.
Johannes

Autor: Andreas Schwarz (andreas) (Admin) Benutzerseite Flattr this
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da ging's doch um Integer, schau mal genauer hin.

Autor: Johannes A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ähm, nagut, ich habe das Teil nur vom Betreff
"Float-Berechnung auf 8051 mit SDCC"
her weder mit int-Rundung noch mit Asm und AVRs
in Zusammenhang gebracht.
Und immer erst genauer hinschauen, fällt mir in
einem Massenforum zugegeben etwas schwer ;-)

Gruß Johannes

Autor: Profi (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Gestern noch ein wenig optimiert.
Und neue Ideen eingebaut, aber noch große Baustelle.
;there are 3 versions, v.1 has the constants within the linear code
(without loop, optimised for speed) and uses 32 Bits,
;V.2 and V.3 use an external table in flash and use a loop for the
digits (optimised for space).
;The result is stored in R6..R15 (for debugging), but could be output
(e.g. to a LCD) instead.
;Then R6 and R7 could be used for full 64-Bit resolution.
;As the result is absolute precise, it can be used for converting any
n-bit-number, not only 10 Bits of the ADC.
;V.3 was optimised by using LPM Rd,x+
;V.4 subtracts 7, 4, 2, 1 times the digit-value, so a maximum of 3
subtracts is necessary (instead of 10 inv.3).
; V.4 uses more table space, but is much faster.
;v.5 is a new begin of a try with constants within the program. It is
very tricky, because it first subtracts 6,
; and then makes a binary search, which guarantees to be finished
within 4 loops for 0..5 and within 3 loops for 6..9.
; But loading all the values eats more cycles than the idea.
; V.5 is not finished, only the first digit is coded. It uses much
space, but should be very fast.
;new ideas: store only the value of 8*digitvalue and calculate 4, 2 and
1* by shifting, which should be much faster
; than loading them from flash

Grüße

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.