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
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.
wenn du mir jetzt noch sagst wie ich im mega8 mit 100 multiplitzieren kann bin ich dir sehr dankbar :D
100 * x = 64 * x + 32 * x + 4 * x lässt sich also mit shiften und addieren durchführen
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.
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):
1 | in r16, ADCH |
2 | ldi r17, 100 |
3 | mul r16, r17 |
4 | ;Ergebnis ist nun in r1:r0, du brauchst aber nur r1 |
5 | in r16, ADCL |
6 | andi r16, 0x7F |
7 | breq PC + 2 |
8 | inc r0 |
9 | ;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.
nicht zum Endergebnis +1 addieren sondern auf den ADC_Wert. Ein ADC_Wert von 0 oder 1 wird immer 0% ergeben. Gruß Hagen
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
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.
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?
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.
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
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
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
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
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.
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%
Korrektur: Bei 8 Bit Auflösung auch nur mit '101' multiplizieren und das low Byte der Multiplikation vergessen.
"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
vielen Dank für die große Hilfe. Werde morgen Ralfs version ausprobieren und die addition mit 0x80 erscheint mir auch als logisch.
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
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
Die Diskussion 1023 vs. 1024 hatten wir doch vor ein paar Tagen schonmal: http://www.mikrocontroller.net/forum/read-1-428358.html#428985
sorry @andreas werde in zukunft die suchfunktion zuerst nutzen. Vielen Dank für die Programme ihr Freax :P
... If you've just spent nearly 30 hours Debugging some assembly, Soon you will be glad to Write in C. ...
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
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!
@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
Ä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
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
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.