Guten morgen zusammen!
Ich habe hier folgendes...naja "Problem" eigentlich nicht, aber ich
bräuchte mal einen Denkanstoß.
Ich habe eine Schaltung für einen PT500. Er ist als einfacher
Spannungsteiler geschaltet, wird über einen NIV-OP verstärkt und geht
dann auf den internen ADC eines MSPs.
- Der ADC hat 12 Bit
- Seine Referenz beträgt 3V
- Mein Messbereich ist zwischen -50°C und +250°C
- Die Verstärkerschaltung liefert mir Werte zwischen 1,237V und 2,915V
- Als ADC-Werte kommen dabei Werte zwischen 1688 und 3979 heraus
Schön und gut...für die eigentliche Frage nicht so wichtig, nur als
Zusatzinformation.
Jetzt zur Rückrechnung: Die Berechnung der Temperatur ist ja nicht
gerade trivial - der Mikrocontroller müsste für Werte >= 0°C einen
ziemlichen Akt leisten...mit Wurzelausdruck usw. Für Werte < 0°C sieht
die Sache noch schlimmer aus, da hier ein Polynom 4. Grades erstmal nach
R umgestellt werden müsste - meiner Meinung nach ist die echnung für
einen uC etwas oversized.
Also: Look-Up-Table! Oder wie seht ihr das?
Ich habe mir jetzt in Excel eine Tabelle erstellt, welche alle
berechneten ADC-Werte für jeden 1°C Schritt enthält - dies ist natürlich
erstmal nur theoretisch, evtl. muss noch ein Korrekturfaktor hinzu, aber
das sei erstmal zweitrangig.
Wie würdet ihr nun vorgehen? Als Beispiel mal ein paar Werte:
1
T[C]|N_ADC|DeltaNzuvorherigemWert
2
1|2100|9
3
2|2108|8
4
3|2116|8
5
4|2124|8
6
....
7
22|2268|8
8
23|2275|7
9
....
Wie ihr seht, so ist das Delta nicht immer gleich - mal sieben, mal
acht...auch mal neun.
Was ich jetzt vorhabe ist, eine LUT anzulegen (in welchen Schritten weiß
ich noch nicht genau, Platz hätte ich im uC genug, um die komplette
Tabelle -50°C bis +250°C zu speichern, mal schauen.
Zwischen den Werten würde ich dann linear interpolieren.
Käme jetzt beispielsweise der Wert 2120 vom ADC, so wären das, linear
interpoliert, 3,5°C.
Wie lege ich nun die Tabelle am besten an und wie suche ich in dieser
nach den zwei Punkten, zwischen den mein eingelesener Wert liegt?
Meine Tabelle hat momentan 301 verschiedene Werte. Mein Index für das
gesuchte Element wäre ja jetzt quasi als Anfang der Wert 1688, dies
könnte ich als Offset abziehen, also quasi:
1
temperatur=temperatur_LUT[ADC-Wert-1688];
Nur so geht das ja halt nicht, da ich ja nicht für jeden ADC-Wert einen
Temperaturwert habe, sondern nur für alle 7-9 Werte sich die Temperatur
um 1°C erhöht.
Damit stehe ich gerade etwas auf dem Schaluch - kann mir da einer
helfen?
Vielen Dank schonmal!
Was hat das Ding denn für eine Funktion (linear ja anscheinend leider
nicht)?
Wenn du die kennst, reichts ja wenn du dir ein paar Werte rauspickst
(z.B. aus so einer Tabelle:
http://www.ephy-mess.de/deutsch/daten/pt500d.htm) und dann dazwischen
interpolierst (mit der Funktion, eben nicht linear). Dann brauchst du
nur wenige Fixwerte.
LUT-Seppel schrieb:> Nur so geht das ja halt nicht, da ich ja nicht für jeden ADC-Wert einen> Temperaturwert habe, sondern nur für alle 7-9 Werte sich die Temperatur> um 1°C erhöht.>> Damit stehe ich gerade etwas auf dem Schaluch - kann mir da einer> helfen?
Du liest deinen Wert ein und dann suchst du die beiden nächsthöheren und
tieferen Werte in deiner Tabelle. Dann berechnest du wie weit du von
denen entfernt bist und interpolierst mit deiner Funktion entsprechend.
Mol schrieb:> aus so einer Tabelle:> http://www.ephy-mess.de/deutsch/daten/pt500d.htm) und dann dazwischen> interpolierst
Ich bin ja eigentlich schon einen Schritt weiter - die Tabelle ist ja
klar. Ich bin ja jetzt im Mikrocontroller und habe hier schon die
ADC-Werte vorliegen. Die Nicht-Linearität des PT500 habe ich ja
berücksichtigt, da ich die ADC-Werte in Excel mit den richtigen
Polynomen berechnet habe.
Wenn du eine handliche Tabelle nehmen möchtest und etwas Rechnen ok ist,
würde ich als Index zum Einspung in die Tabelle
(ADC-Wert >> n) - a nehmen.
Die Wahl von n bestimmt dann die Feinheit der Tabelle und a sorgt dafür,
dass nur der erforderliche Wertebereich abgebildet wird.
In der Tabelle stehen dann die diesem Raster zugeordneten Temperaturen
zwischen denen du mit dem Rest vom ADC-Wert interpolieren kannst.
z.B. ADC-Wert=2268, n=4, a=105
(ADC-Wert >> 4) - 105 = 36, Rest 12
In der Tabellen interpolierst du in diesem Fall zwischen Eintrag 36 und
37 mit den untern 4 Bit vom ADC-Wert.
Gruß
Rainer
Hier sind jetzt von -50°C bis +250°C alle Werte für ganzzahlige
Gradzahlen enthalten.
Wie suche ich jetzt nach bestimmten Elementen? Ich versuche auch einfach
mal - jetzt bitte nicht auf die Syntax achten, ist nur zum Verständnis:
1
floatget_temperature(uint16_tadc_value)
2
{
3
uint8_tfound=0;
4
5
int16_tlower_temp_value;
6
uint16_tupper_adc_value;
7
8
int16_tupper_temp_value;
9
uint16_tupper_adc_value;
10
11
floatreal_temperature;
12
int16_tindex=0;
13
14
do
15
{
16
if(adc_value>=temperature_LUT[index])
17
{
18
lower_temp_value=(index-50)// Speichere Temperatur
So, kann man bestimmt noch verbessern, aber erstmal ob ich es gnerell
verstanden habe, oder ob es grundlegend hakt...
Mol schrieb:> Hast du eine superzeitkritische Anwendung dass du nicht rechnen willst?
Also es ginge bestimmt, aber es ist halt die Frage, ob eine LUT nicht
schneller geht. Mein uC taktet nur mit 1.6864MHz...und der hat noch
einiges zu tun. Wenn der jetzt ewig an ner Wurzel herumrechnet, dann
weiß ich nicht, ob eine LUT nicht besser ist.
Klar kann eine zu lange Liste zu durchsuchen evtl. auch recht lange
dauern.
Werner B. schrieb:> Die Temperatur in der Tabelle z.B. als 1/100 °C ablegen.
Was genau meinst du damit? Also welchen Vorteil habe ich dadurch? Dann
hätte ich doch direkt Fließkommazahlen?
Ich würds so machen wie Rainer es beschrieben hat:
i Wert
-----------------------------------
0 Temperatur für ADC-Wert 1680
1 Temperatur für ADC-Wert 1696
2 Temperatur für ADC-Wert 1712
3 Temperatur für ADC-Wert 1728
4 Temperatur für ADC-Wert 1744
...
...
144 Temperatur für ADC-Wert 3984
145 Temperatur für ADC-Wert 4000
mit genauen z. B. 16-Bit-codierten Temperaturwerten (per Excel
vorberechnet). Ganzzahlige Celsius-Zahlen wären zu grob.
Dann linear interpolieren:
Wenn Du 8 kiB Speicherplatz zur Verfügung hast, könntest Du 4096
vorzeichenbehaftete 16-Bit-Werte abspeichern, die die berechnete
Temperatur in 0.1-Grad-Schritten enthalten.
-50° == -500
+250° == +2500
Zwar sind 8 kiB Tabelle je nach µC sehr viel Speicherplatz, aber
berechnen musst Du dann gar nichts mehr.
Wobei die Tabelle nur für einen Wertebereich zwischen 1688 und 3979
überhaupt gefüllt werden muss, damit schrumpft der Platzbedarf auf 2292
* 2 Byte, also etwa 4.5 kiB.
Dann wäre zu untersuchen, ob es überhaupt erforderlich ist, den Wandler
mit voller 12-Bit-Auflösung zu betreiben. Wenn Du eh nur um die 300
verschiedene Werte auflösen möchtest, könnte eine Halbierung oder gar
Viertelung des ADC-Wertes, also eine Auflösungsreduktion auf 11 oder 10
Bit auch helfen, die Tabelle weiter zu verkleinern. Dann belegt sie 2292
oder nur 1146 Bytes.
Es kommt eigentlich auf deine Meßaufgabe drauf an:
Wenn du richtig schnell sein mußt, ist eine Tabelle, in die du direkt
mit einem passend genauen ADC-Wert als Index reingehen kannst, die
Methode der Wahl.
Wenn du Zeit für eine Multiplikation mit dem 4-Bit Rest hast, ist die
Interpolation wie Vuvuzelatus sie skizziert hat, am einfachsten. Die
Multiplikation der Temperaturtabellenwerte (Genauigkeit entsprechend der
wahren Genauigkeit eines PT-500) mit einer 4Bit Zahl ist auch gut "von
Hand" optimiert umsetzbar.
Gruß
Rainer
LUT-Seppel schrieb:> Wie würdet ihr nun vorgehen? Als Beispiel mal ein paar Werte:> T[C] | N_ADC | Delta N zu vorherigem Wert> 1 | 2100 | 9> 2 | 2108 | 8> 3 | 2116 | 8> 4 | 2124 | 8> ....> 22 | 2268 | 8> 23 | 2275 | 7> ....> Wie ihr seht, so ist das Delta nicht immer gleich - mal sieben, mal> acht...auch mal neun.
Als Denkanstoß: erst grob an die richtige Stelle der Tabelle springen,
dann vollends zur richtigen Stelle hochzählen.
Lege die LUT gradweise im Speicher ab (d.h. je 1°C Temperaturerhöhung
ein Wert).
Nimm nach dem Einlesen den ADC-Wert, subtrahiere den Offset (die 1688)
und dividiere den Wert durch 8 (oder einem anderen Kompromiss aus
größtes Delta N und Umsetzbarkeit der Division in Festkomma). Ergebnis
ist ein erster Tabellenindex. Damit springst Du dann in die Tabelle und
zählst den Index so lange hoch, bis der eingelesene Wert größer als
N_ADC ist. Fertig.
Den initialen Tabellenindex musst Du ggf. noch ein bisschen nach unten
korrigieren, um garantiert immer unterhalb der richtigen Stelle zu
landen.
Vorteil: Du musst nicht die ganze Tabelle durchlaufen, trotzdem ist der
Rechenaufwand überschaubar.
Max G. schrieb:> Lege die LUT gradweise im Speicher ab (d.h. je 1°C Temperaturerhöhung> ein Wert).
Warum so rum?
Ich würde in die Tabelle den Gradwert pro ADC-Wert eintragen, und zwar
die ab 1688. Wenn genug Platz ist, sieht das als Software dann so aus
grad = Tabelle[ADC - 1688]
MfG Klaus
So ein Zufall, genau das selbe habe ich gestern auch gemacht.
Da die Tabelle ja stetig ist, bietet sich eine binäre Suche an.
Anpassen, ob sie fallend oder steigend ist (hier fallend).
ADC=515; //for testing
idx=64; //middle of table
step=32;
do
if(tab[idx]>ADC)idx+=step; else
if(tab[idx]<ADC)idx-=step; else break;
while((step/=2));
printf("\r\n%i",idx);
//Interpolieren
printf(" %4i %4i %4i %4i %9.3f",ADC,tab[idx-1],tab[idx],
tab[idx+1],(float)(ADC-tab[idx])/(tab[idx+1]-tab[idx]));
> mit genauen z. B. 16-Bit-codierten Temperaturwerten (per> Excel vorberechnet). Ganzzahlige Celsius-Zahlen wären zu grob.
So mache ich es auch.