Forum: Mikrocontroller und Digitale Elektronik Hilfe bei Look-Up-Table benötigt


von LUT-Seppel (Gast)


Lesenswert?

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  | Delta N zu vorherigem Wert
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!

von Mol (Gast)


Lesenswert?

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.

von Mol (Gast)


Lesenswert?

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.

von LUT-Seppel (Gast)


Lesenswert?

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.

von Mol (Gast)


Lesenswert?

Was geht denn nicht?
Hast du eine superzeitkritische Anwendung dass du nicht rechnen willst?

von Werner B. (werner-b)


Lesenswert?

Die Temperatur in der Tabelle z.B. als 1/100 °C ablegen.

von Rainer (Gast)


Lesenswert?

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

von LUT-Seppel (Gast)


Lesenswert?

OK, also dann versuche ich mal...

Hier nochmal die Werte:
1
T[C]  |  N_ADC  | Delta N zu vorherigem Wert
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
  ....
Also mache ich eine LUT:
1
uint16_t temperature_LUT [301] = {..., 2100, 2108, 2116, 2124, ..., 2268, 2275, ...}
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
float get_temperature (uint16_t adc_value)
2
{
3
  uint8_t found = 0;
4
5
  int16_t lower_temp_value;
6
  uint16_t upper_adc_value;
7
8
  int16_t upper_temp_value;
9
  uint16_t upper_adc_value;
10
11
  float real_temperature;
12
  int16_t index = 0;
13
14
  do
15
  {
16
    if (adc_value >= temperature_LUT [index])
17
    {
18
      lower_temp_value = (index - 50) // Speichere Temperatur
19
      lower_adc_value = temperature_LUT [index]; // Speichere ADC-Wert
20
21
      upper_temp_value = (index - 49) // Speichere Temperatur +1°
22
      upper_adc_value = temperature_LUT [index +  1]; // Speichere ADC-Wert
23
24
      found = 1;
25
    }
26
    
27
    index++;
28
  } while (!found);
29
30
  real_temperature = interpolate (lower_temp_value, lower_adc_value, upper_temp_value, upper_adc_value);
31
32
  return real_temperature;
33
}

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?

von Achim M. (minifloat)


Lesenswert?

Das hier schon mal angesehen?
http://de.wikipedia.org/wiki/Linearisierung_von_resistiven_Sensoren
Eine recht detaillierte Anleitung wie man mit Hilfe einer 
Tabellenkalkulation die Temperatur als Funktion von U_ADC darstellen 
kann.
mfg mf

von Vuvuzelatus (Gast)


Lesenswert?

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:
1
i = ADC >> 4     // Tabellenindex, i = 0...145
2
f = ADC & 0xF    // Faktor für Interpolation, f = 0...15
3
4
DeltaT = Tabelle[i+1] - Tabelle[i]
5
6
T = Tabelle[i] + ((f*DeltaT) >> 4)

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Mitleser (Gast)


Lesenswert?

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

von Max G. (l0wside) Benutzerseite


Lesenswert?

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.

von Klaus (Gast)


Lesenswert?

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

von eProfi (Gast)


Lesenswert?

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.

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
Noch kein Account? Hier anmelden.