PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Berechnung mit long-Variable fehlerhaft



Suggarman
28.03.2009, 07:29
Hallo,

ich bilde einen Mittelwert aus 256 Messungen eines 12-bit-AD-Wandlers.
Hierzu summiere ich die 256 Messwerte auf und teile anschließend die Summe durch 256. Die Summe ist also immer im Bereich 0 bis 1048320 (=256*4095). Eigentlich müsste das in eine long-Variable passen. Die Berechnung ergibt aber unsinnige Werte. Deklariere ich die Summenvariable als single, funktioniert alles einwandfrei.

Hat jemand eine Idee, warum das so ist?

mfg

Stefan

oe9vfj
28.03.2009, 08:13
Ich habe die Aufsummierung einer Long-Variable und Division durch 256 getestet und keinen Fehler gefunden.
Da müsste man eventuell Deinen verwendeten Code sehen, um hier weiterhelfen zu können.

In Deinem Fall, wo Du 256 Einzelwerte für die Durchschnittberechnung benutzt, kann der Mittelwert sogar ohne Division ermittelt werden.
Eine Division durch 256 ist eine Verschiebung der 4 Einzelbytes um jeweils 1 Position nach rechts. Das heißt der Durchschnittswert sind die Bytes 2, 3 und 4 der Long-Variable und einer 0 als neues Most-Significant Byte.
Dieser Fall kann mit einem OVERLAY gelöst werden.


Dim lSumme as Long
Dim bDummy as Byte
Dim lDurchschnitt as Long at lSumme + 1 overlay
Dim lEinzel as Long

lSumme = 0
for lEinzel = 15000 to 15255
lSumme = lSumme + lEinzel
next

print lDurchschnitt

Das bDummy ist nötig, damit lDurchschnitt als 4. Byte (MSB) immer einen definierten 0-Wert hat.


Wenn die Anzahl der Einzelmessungen für den Durchschnittwert eine 2-er Potenz ist, also 2, 4, 8, 16, 32, 64 ..... dann kann mit einem SHIFT RIGHT die Division ersetzt werden. Hier ein Beispiel mit 64 Einzelmessungen.


Dim lEinzel as Long
Dim lSumme as Long
Dim lDurchschnitt as Long

lSumme = 0

For lEinzel = 15000 to 15063
lSumme = lSumme + lEinzel
next

Shift lSumme, RIGHT, 6

lDurchschnitt = lSumme

Print lDurchschnitt

Da eine Division vergleichsweise eine zeitaufwendige Berechnung ist, lässt sich mit diesen beiden Methoden besonders in zeitkritischen Applikationen Rechenzeit sparen.

Suggarman
28.03.2009, 10:15
Danke für Deine Mühen.

Hier mal der Code-Ausschnitt:




Const Max127wr = 84
Const Max127rd = 85
Const Controlbytequer = &B11100000 'Ch6, 0 bis 5V, normal Operation
Const Controlbytehoehe = &B11110000 'Ch7, 0 bis 5V, normal Operation
Const Controlbytedruck = &B10110000 'Ch3, 0 bis 5V, normal Operation
Const Controlbyteakku = &B11000000 'Ch4, 0 bis 5V, normal Operation


Const Controlbytegyrox = &B10010000 'Ch1, 0 bis 5V, normal Operation Vielleicht falsch herum !!!!!!!!!!!!!!!!
Const Controlbytegyroy = &B10100000 'Ch2, 0 bis 5V, normal Operation



Dim I As Byte , Accsumme As Single
Dim Wert As Word , Wert2 As Word

Dim Highbyte As Byte , Lowbyte As Byte

_lcd_e = 128 'Upper half of 4-line display is selected



Declare Function Max127lesen(byval Chsetting As Byte) As Word


Accsumme = 0
For I = 0 To 255
Wert = Max127lesen(controlbytequer)
Accsumme = Accsumme + Wert
Next
Wert = Accsumme / 256
Locate 1 , 1
Lcd "Quer : " ; Wert ; " "


end

Function Max127lesen(byval Chsetting As Byte) As Word
Local Adwert As Word
I2cinit
I2cstart
I2cwbyte Max127wr
I2cwbyte Chsetting
I2cstop
I2cstart
I2cwbyte Max127rd
I2crbyte Highbyte , Nack
I2crbyte Lowbyte , Nack
I2cstop
Adwert = Highbyte * 16
Lowbyte = Lowbyte / 16
Adwert = Adwert + Lowbyte
Max127lesen = Adwert
End Function




Der "Ruhewert" lliegt bei ca 1900. Wenn ich die Variable ACCSumme als "long" deklariere, kommen nur noch 3-stellige Ergebnisse heraus.

Die Zahl 256 habe ich bewusst gewählt, weil ich dachte, der Controller/Bascom rechnet dann mittels Verschieben der bits.

for_ro
28.03.2009, 14:13
Ohne es ausprobiert zu haben denke ich, dass das an deinen impliziten Typ-Wandlungen liegt:
Wert = Accsumme / 256
Probier mal so:
Accsumme = Accsumme / 256
Wert = Accsumme

Gruß

Rolf

oe9vfj
28.03.2009, 14:35
Ich denke, Rolf hat Recht. Zuerst werden die Variablen in den Typ der Zielvariable umgewandelt und bei LONG --> WORD, die LONG links beschnitten und damit bei Werten über 65535 verändert.

Bei Deiner Variante mit der Voraussetzung, dass der aufsummierte Wert nicht größer als 16777215 (2^24 - 1) wird, kann die OVERLAY - Variante noch etwas vereinfacht werden:


Dim lSumme as Long
Dim wDurchschnitt as Word at lSumme + 1 overlay
Dim lEinzel as Long

lSumme = 0
for lEinzel = 3000 to 3255
lSumme = lSumme + lEinzel
next

print wDurchschnitt

Hier wird der Durchschnittswert gleich als WORD definiert und ist damit innerhalb des SRAM-Bereiches der LONG-Variable, welche die aufsummierten Werte aufnimmt.

for_ro
28.03.2009, 15:16
Dim wDurchschnitt as Word at lSumme + 1 overlay
Ziemlich clevere Lösung für das Teilen durch 256!

Gruß

Rolf

Suggarman
28.03.2009, 19:15
Danke für Eure Überlegungen. Da ich die Hardware im Büro habe, kann ich es erst am Montag testen. Die Art und Weise, wie typverschiedene Variablen übergeben geben war mir nicht bekannt. Ich beschäftige mich erst seit ein paar Wochen mit Bascom und "übersetze" ein C-Control-Project auf ein RN-Mega2560.

Stefan

Suggarman
30.03.2009, 07:28
Ohne es ausprobiert zu haben denke ich, dass das an deinen impliziten Typ-Wandlungen liegt:
Wert = Accsumme / 256
Probier mal so:
Accsumme = Accsumme / 256
Wert = Accsumme


Du hast recht, so geht es.



Dim lSumme as Long
Dim wDurchschnitt as Word at lSumme + 1 overlay
Dim lEinzel as Long

lSumme = 0
for lEinzel = 3000 to 3255
lSumme = lSumme + lEinzel
next

print wDurchschnitt

Diese Variante ist natürlich wunderbar. Wenn ich bei 256 Messungen bleibe, ist diese Variante natürlichg erste Sahne.

Vielen Dank für Eure Bemühungen.


Stefan

oe9vfj
30.03.2009, 16:40
Wenn Du mit wDurchschnitt weiterarbeiten willst, nachdem die lSumme wieder verändert wird (neuer Meßzyklus) musst diese in eine eigene Word-Variable umspeichern. Mit jeder Änderung von lSumme wird natürlich auch wDurchschnitt (überlagert zu lSumme) verändert.

Suggarman
30.03.2009, 20:13
Ja, danke. Das ist mir schon klar.

mfg

Stefan