Forum: Mikrocontroller und Digitale Elektronik NTC Erfassung ohne Fließkomma-Arithmetik


von Bernd K. (bkohl)


Lesenswert?

Die Code-Zeilen mit double-Arithmetik kosten ganze 1500bytes.
Hat jemand eine Idee zu einer codesparenden und trotzdem genauen 
Alternative?

double adc2Cel(long adcval){ // 10bit ADC > °C    1.5kByte - ganz schön 
viel
#define R25_ntc 10000        // NTC Nennwiderstand
#define B_ntc  3988          // NTC-Empfindlichkeit
#define Tn_ntc  298          // Bezugstemperatur
#define R  10000L            // Gegenwiderstand
    double s=0;
    long int rntc,tntc;
  if (adcval<10) adcval=10;                    // sinnvolle Grenzwerte
  if (adcval>1010) adcval=1010;                // -47 bis  182 °C
  rntc = ((long)adcval*R)/(1024-adcval);       // Rntc
  s = B_ntc + log((double)rntc/R25_ntc)*Tn_ntc;
  tntc= (long)1000*B_ntc*Tn_ntc/(long)s;
  s= (tntc-273150)/1000;
  return s;                                    // °C
}

Dank euch!

Bernd

von Mike (Gast)


Lesenswert?

Bernd Kohlstrunk schrieb:
> Hat jemand eine Idee zu einer codesparenden und trotzdem genauen
> Alternative?

Dann schreib doch mal auf, was du rechnen möchtest. Oder soll das jetzt 
jeder hier aus deinem Code reverse-engineeren?

von Jim M. (turboj)


Lesenswert?

Klarer Fall für eine Wertetabelle.

Einfach in Excel (oder LibreOffice) die erwarteten ADC Werte für 
ganzzahlige Temperaturwerte ausrechnen und als Array (im Flash) 
speichern.

Der Code muss nur die Position in der Tabelle finden, für die Tabelle[x] 
<= adc_wert <= Tabelle[x+1] gilt, und dann linear interpolieren für 
einen recht genauen Temperaturwert.

Die Tabelle bräuchte allerdings für 230 Temperaturwerte 460 Byte 
Speicherplatz. Kannst Du den Bereich weiter einschränken? Können 
Temperaturen >100°C überhaupt auftreten?

von Bernd K. (bkohl)


Lesenswert?

Ja Entschuldigung! Es geht um die T-R Beziehung von Heißleitern:

T= B*Tn / (B + ( ln (Rt/Rn) ) * Tn)

Der Kern der Frage ist, wie man long-Variablen logarithmieren kann ohne 
arg an Genauigkeit zu verlieren. Eine "eingebaute" Funktion gibt es 
offenbar dafür nicht.

von m. keller (Gast)


Lesenswert?

Wie wäre es mit Festkommaarithmetik? Ich vermute du verwendest einen 
8bit Controller (AVR z.B.) und die float-Lib sowie die float/double 
Rechnerrei frisst schon alleine ziemlich speicher.

Also du verrechnest alle Variablen welche eine Kommastelle haben mit 
einem festen Faktor. Je nach dem wie groß die Auflösung sein soll: 10, 
100, 1000. Datentyp muss dann aber am besten int32 oder int64 sein.

Dann solltest du die Rechnung zerlegen um zu schauen ob du irgendwo eine 
integer-Überlauf bekommst. Wenn ja muss du die Berechnung in 
Teilschritte erledigen.

von Noch einer (Gast)


Lesenswert?

> long-Variablen logarithmieren
Am schnellsten geht wohl auf die Basis 2 umrechnen und schieben.

Noch schneller geht aber Jims Vorschlag, vorberechnete Werte 
interpolieren.

von m.n. (Gast)


Lesenswert?

Bernd Kohlstrunk schrieb:
> Die Code-Zeilen mit double-Arithmetik kosten ganze 1500bytes.

Na und? Reicht der Speicher nicht?
Falls Dir sechs Stellen Auflösung reichen, rechne doch mit 'float' ;-)

von Helmut S. (helmuts)


Lesenswert?

> ... , rechne doch mit 'float' ;-)

Die 8bit AVR Mikrocontroller rechnen eh nur mit "float" auch wenn man 
"double" im C-Programm schreibt.

von m.n. (Gast)


Lesenswert?

Helmut S. schrieb:
> Die 8bit AVR Mikrocontroller rechnen eh nur mit "float" auch wenn man
> "double" im C-Programm schreibt.

Von Hause aus rechnen sie nur mit 8 Bits. Aber man kann damit auch 
double Berechnungen machen, wenn man den passenden Compiler verwendet.

von Bernd K. (bkohl)


Lesenswert?

hab was gefunden. Hier wurde das Thema mit dem Logarithmus schon mal 
beackert:

Beitrag "Code für Logarithmus gesucht"

Danke für alle Reaktionen.

Bernd

von Bernd K. (bkohl)


Lesenswert?

Dank des Beitrages: Beitrag "Algorithmus für Logarithmus-Bererechnung?"
bin ich zu einer Lösung gekommen, die auf meinem Tiny 1.5kbyte spart.
Die feste Skalierung in ln_m() ist natürlich ein Kompromiss. Im 
mittleren Temperaturbereich ist aber eine Auflösung von 0.1 °C gut 
erreichbar.

long adc2_mC(long adcval){    // 10bit ADC > T in milli°C Einheiten
#define B_ntc    3988L        // NTC-Empfindlichkeit in K
#define Tn_ntc    298L        // 25°C Bezugstemperatur in K
#define Rn_ntc  10000L        // 10k Widerstand bei Bezugstemperatur
#define Tzero  273150L        // mK
#define R       10000L        // 10k Gegenwiderstand der Halbbruecke
long rntc,tntc,ne;
    if (adcval<10) adcval=10;      // 194.1°C
    if (adcval>1010) adcval=1010;   // -47.3°C
    rntc = ((long)adcval*R)/(1024-adcval);          // Rntc 1/1000 genau
    ne = B_ntc + (ln_m((rntc*1000)/Rn_ntc)*Tn_ntc)/1000; // Nenner in K
    tntc= (1000*B_ntc*Tn_ntc)/ne;   // mK= mK*K/K
    return (tntc-Tzero);      // mC
}

long ln_m(long x){// In/Output als milli-Zahlen von 
10(=0.01)..100000(=100)
#define SCALE 9    // Erweitern mit 2^SCALE und später SCALE abziehen
long lval;    // (Logarithmengesetz)
uint32_t rval;
   lval=(x*(1<<SCALE))/1000;  //
   if (lval>0xFFFF) lval=0xFFFF; if (lval<1) lval=1;// Begrenzen
   rval=fixlog2((uint16_t)lval);  // Aufruf mit Typ-Anpassung
   lval=(rval >> 16)*1000000 + ((rval & 0xffff) * 15625 >> 10) - 
SCALE*1000000;// ppm genau -9...7
   lval=((lval/100)*6931)/100000; // Umrechnung log2->ln -6.238 bis 
4.852
   return lval ;      // Ausgabe als milli-Zahl
}

uint32_t fixlog2(uint16_t x) { // Input 0...64k   Output: Fixkomma 16.16
  uint32_t q;                          // 2.30
  uint16_t y = 0;                      // 0.16
  uint8_t intpart = 15, i;             // 8.0

  while(!(x & 0x8000)) {x <<= 1;intpart--;} // die Ganzen ermitteln

  for(i=0; i<16; i++) {
    q = (uint32_t)x * x;               // x: 1.15, q: 2:30
    y <<= 1;
    if(q & (uint32_t)1 << 31)          // q >= 2.0?
      y |= 1;                          // 1-Bit -> y, q *= 2, q: 1.31
    else
      q <<= 1;                         // 0-Bit -> y, q: 1.31
    x = q >> 16;                       // x = q
  }
  return (uint32_t)intpart << 16 | y;  // return: 16.16
}  // Quelle: Beitrag "Algorithmus für Logarithmus-Bererechnung?"

von m.n. (Gast)


Lesenswert?

Bernd K. schrieb:
> bin ich zu einer Lösung gekommen, die auf meinem Tiny 1.5kbyte spart.

Immerhin wissen wir jetzt, daß es sich um einen AVR handelt. Da aber 
auch schon kleine ATtiny mit 8 kB ROM verfügbar sind, sehe ich keinen 
Sinn darin, den notwendigen Code einsparen zu wollen.

Vergleicht man die gezeigten Quelltexte, geht bei der nun favorisierten 
Lösung die Les- und Wartbarkeit völlig in die Hose. Welche Fehler eine 
Programmänderung mit sich bringt, ist nicht abzusehen.
Für mich ist das eine schlechte Lösung!

von Bernd N (Gast)


Lesenswert?

Laß halt den Logarithnmus weg. Alles was du brauchst steht schon in 
deinem Code.

adcval=10 = 194.1°C
adcval=1010 = -47.3°C

Und daraus machst du eine einfache Gleichung.

Beispiel:
http://www.mikrocontroller.net/attachment/127684/msp430x2xxTemperatur.c

von Jim M. (turboj)


Lesenswert?

Bernd N schrieb:
> adcval=10 = 194.1°C
> adcval=1010 = -47.3°C
>
> Und daraus machst du eine einfache Gleichung.

Die NTCs sind leider alles andere als linear. So einfach geht es IMHO 
nicht.

von W.S. (Gast)


Lesenswert?

m.n. schrieb:
> Na und? Reicht der Speicher nicht?
> Falls Dir sechs Stellen Auflösung reichen, rechne doch mit 'float' ;-)

Du Scherzbold!
Der TO fummelt mit nem AVR herum und dort gibt es sowieso kein double, 
sondern double wird einfach auf float heruntergebrochen.

Mein erster Tip wäre, einen simplen PIC16Fxxx zu nehmen, denn für diese 
µC hatte ich hier schon mal ne Gleitkomma-Lib gepostet.

Mein zweiter Tip wäre, sich mal die Steinhart-Hart-Formel aus dem Netz 
zu ziehen und zu benutzen. 1,5K für ne GK-Lib ist m.E. zwar relativ viel 
(wohl schlampig implementiert), aber allemal besser als irgendwelche 
Integer- Fummel-Varianten, die vor lauter internen Überläufen mehr 
schlecht als recht funktionieren.

Hatte da nicht mal jemand vor einiger Zeit geschrieben, daß man 
Gleitkomma bei µC seiner Ansicht nach überhaupt nicht braucht???

W.S.

von MatthiasS_1970 (Gast)


Lesenswert?


von Pandur S. (jetztnicht)


Lesenswert?

Erst sollte man mal das Problem spezifizieren und nicht gleich die 
Loesung vorwegnehmen. Also, man moechte einen NTC im Bereich von -47 bis 
182 °C betreiben, glaub ich gleich mal nicht. Und dann noch 0.1 Grad 
anzeigen wollen. Aha. Mit einem 10bit ADC. Sonst noch was ?

Woher kommen die -47 Grad ? Von einem Peltier ? Da muss man gut sein. 
Einen 2 stoeckigen und sehr gute Isolation. Nicht trivial.
Die 180 Grad ? Ein Epoyk NTC ist nur bis 150 oder so spezifiziert.
Mit 10 bit? Nie. NIE ! Schreib mal eine Simulation der Werte, und der 
Aufloesung auf dem PC. Aus dem Bauch heraus sag ich mal, die 10 bit 
reichen nicht mal fuer 1 Grad Aufloesung. Die Genauigkeit lassen wir mal 
weg.

von Udo S. (urschmitt)


Lesenswert?

Jetzt Nicht schrieb:
> Und dann noch 0.1 Grad anzeigen wollen. Aha. Mit einem 10bit ADC.
War auch mein Gedanke. Die "Auflösung" kann der TE ja bei der Ausgabe 
haben, was die dann aber mit dem realen Wert zu tun hat ist eine ganz 
andere Frage.

von m.n. (Gast)


Lesenswert?

W.S. schrieb:
> m.n. schrieb:
>> Na und? Reicht der Speicher nicht?
>> Falls Dir sechs Stellen Auflösung reichen, rechne doch mit 'float' ;-)
>
> Du Scherzbold!
> Der TO fummelt mit nem AVR herum und dort gibt es sowieso kein double,
> sondern double wird einfach auf float heruntergebrochen.

Wie ich sehe, hast Du den Scherz aber nicht verstanden ;-)
Mit einem AVR kann man durchaus 'double' mit 64 Bit rechnen und die Lib 
dafür ist etwas größer als die für 32 Bit 'float'.

Für mich bleibt es unsinnig, die Codegröße ohne Not 'optimieren' zu 
wollen. Auch die Ersatzlösungen brauchen Speicherplatz und haben hohes 
Fehlerpotential.
Jetzt fehlt noch das Totschlagargument, float wäre viel zu langsam. 
Vorweg: das ist genau so unsinning wie falsch!

von Pandur S. (jetztnicht)


Lesenswert?

Ich rechne auch NTC, allerdings nur 3 mal pro Sekunde.
Ich rechne die Linearisierung mit float, 32bit, aus
folgenden Gruenden:
- Ein approximierendes Polynom muesste fuer die gewuenschte
  Aufloesung und Genauigkeit 6.Ordnung sein. Der
  Linearisierungspunkt waere dann aber ohne Float nicht
  mehr frei waehlbar.
- Ich verwende einen 24bit Wandler
- ich moechte besser als 5mK Aufloesung, zum Regeln
- der NTC ist spezifiziert als 1%
- die absolute Genauigkeit ist vielleicht 2 Grad, begrenzt
  durch den NTC

Alle nachfolgenden Berechnungen gehen dann mit 32 bit
Integern.

von Peter D. (peda)


Lesenswert?

Bernd K. schrieb:
> Die Code-Zeilen mit double-Arithmetik kosten ganze 1500bytes.

Wo ist das Problem, schon die 8-Pinner AVR haben bis zu 8kB Flash.
Mußt Du unbedingt einen 6-Pinner verwenden?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Bernd K. schrieb:
> Hat jemand eine Idee zu einer codesparenden und trotzdem genauen
> Alternative?
Ich würde eine Lookuptabelle und lineare Interpolation vorschlagen.

Definiere "genau".
Bernd K. schrieb:
> if (adcval<10) adcval=10;      // 194.1°C
> if (adcval>1010) adcval=1010;   // -47.3°C
Vergiss diese Kommastellen ganz schnell. Vermutlich hat nämlich dein NTC 
schon 5% Fehler...

von Peter D. (peda)


Lesenswert?

SMT-160-30-18
Nicht gerade günstig, aber ne super Auflösung.

von Klaus (Gast)


Lesenswert?

Lothar Miller schrieb:
> Vergiss diese Kommastellen ganz schnell. Vermutlich hat nämlich dein NTC
> schon 5% Fehler...

Wobei das nur der einfachere Teil des Problems ist (daß man mit einem 
anderen Sensor verbessern kann). Wo die Temperatur gemessen wird ist ein 
viel größeres Problem. Selbst innerhalb eines festen Körpers sind 
Temperaturgradienten von mehreren Grad, die dann auch noch je nach 
Wetterlage unterschiedlich sind, nichts ungewöhnliches. Statt 
aufwändiger Rechnungen kann man auch die Nachkommastellen aus 
/dev/random lesen.

MfG Klaus

von Bernd K. (bkohl)


Lesenswert?

Vielen Dank für die Hinweise!
Das schöne am NTC ist ja seine Nichtlinearität.
Im konkreten Einsatz als Drainback-Steuerung einer Thermischen 
Solaranlage ist das sehr praktisch. Außerdem werden dort üblicherweise 
nur PT100 oder NTCs verbaut. Die Anforderung bestand aus zwei Punkten:
1. genaue Messungen um die 0°C (Frostschutzfunktion) und
2. über den ganzen real auftretenden T-Bereich von sagen wir mal -30 bis 
+150°C überhaupt Meßergebnisse zu bekommen und keinen Over-/Underflow.

LUTs sind sicher ein pragmatischer Ansatz. Der Weg über den 
Festpunkt-Logarithmus funktioniert aber auch.

OK, Profis nehmen einfach einen größeren Prozessor und haben die 
Probleme nicht. Hab mich aber auf den ATtiny85 mit Funk-Interface HM-TRP 
eingeschossen. Ich finde es immer wieder beeindruckend, was man aus dem 
Kistchen rausholen kann. Dank dem Bootloader von Peter Dannegger fürs 
Update via Klinkenbuchse und Debugging via Soft-UART (beides 1-Wire), 
kommt man trotz der knappen Resourcen schnell zum Erfolg. Man darf nur 
nicht auf die Idee kommen, dicke Libs einzubinden.

8 Pins sind wenig, aber es reicht mit ein Paar Kniffen für die 
Ansteuerung/Auslesung von:
1x Ventil
1x Pumpe
1x NTC
1x Messung von Pumpen oder Ventilstrom
3 Tasten
2 LEDs
1x RS232 (1-Wire)
1x Servomotor mit Stromüberwachung
... und der Resetpin ist immer noch aktiv :-)

Wen es interessiert, bitte melden.

von Maxx (Gast)


Lesenswert?

Jetzt Nicht schrieb:
> - Ein approximierendes Polynom muesste fuer die gewuenschte
>   Aufloesung und Genauigkeit 6.Ordnung sein.

Eh?

- Der Fehler der Approximination nimmt bei höheren Graden zu
- Die Auflösung ist unabhängig vom Grad
- Üblicherweise erhöht man die Anzahl der Stützstellen bei konstanten 
Grad (Siehe Splines, Teillinearisierung, etc)

>   Der
>   Linearisierungspunkt waere dann aber ohne Float nicht
>   mehr frei waehlbar.

Quatsch. Natürlich ist er genauso frei wählbar. Float stellt einen 
Teilbereich der reellen Zahlen (also p/q mit p und q aus den Ganzen 
Zahlen) dar. Den geforderten Bereich -50 bis +200 lässt sich 
hervorragend mit < 0.01er Granularität darstellen. Bei konstantem q 
bleibt reine Integer Arithmetik. Bei gleicher Speichergröße, lässt sich 
hier sogar eine kleinere Granularität erreichen und damit die Stütz- und 
Interpolationsstellen freier wählen.

von Pandur S. (jetztnicht)


Lesenswert?

>>   Der
>   Linearisierungspunkt waere dann aber ohne Float nicht
>   mehr frei waehlbar.

>Quatsch.

Aber nicht im Feld als Parameter, denn dann wuerde man ja wieder float 
brauchen um das Polynom zu rechnen.

von m.n. (Gast)


Lesenswert?

Jetzt Nicht schrieb:
> Ich rechne die Linearisierung mit float, 32bit, aus
> folgenden Gruenden:

> Alle nachfolgenden Berechnungen gehen dann mit 32 bit
> Integern.

Das ist dann eigentlich egal, weil die float-LIB schon eingebunden ist 
und der Code nicht mehr nennenswert größer wird.

von Maxx (Gast)


Lesenswert?

Jetzt Nicht schrieb:
>>>   Der
>>   Linearisierungspunkt waere dann aber ohne Float nicht
>>   mehr frei waehlbar.
>
>>Quatsch.
>
> Aber nicht im Feld als Parameter, denn dann wuerde man ja wieder float
> brauchen um das Polynom zu rechnen.

Nein, warum 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.